import { Router } from 'express';
import multer from 'multer';
import path from 'path';
import fs from 'fs';
import bcrypt from 'bcryptjs';

import Paths from '@src/common/constants/Paths';
import { prisma } from '@src/common/database';
import { jsonResponse } from '@src/common/util/bigint-serializer';
import { getCurrentUser, requireAdminOrPartner, requireAdmin } from '@src/common/middleware/auth';
import adminPayRouter from './admin-pay';
import { sendToChatRoom } from './common/chat-sse';
import { isValidMemberLevel, getAllMemberLevels } from '@src/utils/memberLevel';
import {
  compressCoverImage,
  compressChatImage,
  compressGiftIcon,
  compressAdvertisementImage
} from '@src/utils/imageCompression';

const router = Router();

// 应用用户认证中间件到所有admin路由
router.use(getCurrentUser);
router.use(requireAdminOrPartner);

// 配置 multer 存储
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    const uploadPath = file.fieldname === 'video'
      ? path.join(__dirname, '../../uploads/videos')
      : path.join(__dirname, '../../uploads/covers');

    // 确保目录存在
    if (!fs.existsSync(uploadPath)) {
      fs.mkdirSync(uploadPath, { recursive: true });
    }
    cb(null, uploadPath);
  },
  filename: (req, file, cb) => {
    const timestamp = Date.now();
    const ext = path.extname(file.originalname);
    const name = `${timestamp}-${Math.random().toString(36).substring(2)}${ext}`;
    cb(null, name);
  }
});

const upload = multer({
  storage,
  limits: {
    fileSize: 50 * 1024 * 1024, // 50MB - 对于图片来说足够了，视频文件另外处理
  },
  fileFilter: (req, file, cb) => {
    if (file.fieldname === 'video') {
      const allowedTypes = ['.mp4', '.mov'];
      const ext = path.extname(file.originalname).toLowerCase();
      if (allowedTypes.includes(ext)) {
        cb(null, true);
      } else {
        cb(new Error('mp4와 mov 형식의 비디오 파일만 지원됩니다'));
      }
    } else if (file.fieldname === 'cover') {
      const allowedTypes = ['.jpg', '.jpeg', '.png', '.webp'];
      const ext = path.extname(file.originalname).toLowerCase();
      if (allowedTypes.includes(ext)) {
        cb(null, true);
      } else {
        cb(new Error('jpg, png, webp 형식의 커버 이미지만 지원됩니다'));
      }
    } else if (file.fieldname === 'cover-upload') {
      const allowedTypes = ['.jpg', '.jpeg', '.png', '.webp']
      const ext = path.extname(file.originalname).toLowerCase()
      if (allowedTypes.includes(ext)) {
        cb(null, true)
      } else {
        cb(new Error('jpg, png, webp 형식의 커버 이미지만 지원됩니다'))
      }
    } else if (file.fieldname === 'image') {
      const allowedTypes = ['.jpg', '.jpeg', '.png', '.webp', '.gif']
      const ext = path.extname(file.originalname).toLowerCase()
      if (allowedTypes.includes(ext)) {
        cb(null, true)
      } else {
        cb(new Error('jpg, png, webp, gif 형식의 이미지만 지원됩니다'))
      }
    } else if (file.fieldname === 'icon') {
      const allowedTypes = ['.jpg', '.jpeg', '.png', '.webp']
      const ext = path.extname(file.originalname).toLowerCase()
      if (allowedTypes.includes(ext)) {
        cb(null, true)
      } else {
        cb(new Error('jpg, png, webp 형식의 아이콘만 지원됩니다'))
      }
    } else {
      cb(new Error('알 수 없는 파일 필드입니다'));
    }
  }
});

// 视频上传的单独配置（允许更大的文件）
const videoUpload = multer({
  storage,
  limits: {
    fileSize: 2 * 1024 * 1024 * 1024, // 2GB - 仅用于视频文件
  },
  fileFilter: (req, file, cb) => {
    if (file.fieldname === 'video') {
      const allowedTypes = ['.mp4', '.mov'];
      const ext = path.extname(file.originalname).toLowerCase();
      if (allowedTypes.includes(ext)) {
        cb(null, true);
      } else {
        cb(new Error('mp4와 mov 형식의 비디오 파일만 지원됩니다'));
      }
    } else if (file.fieldname === 'cover') {
      const allowedTypes = ['.jpg', '.jpeg', '.png', '.webp'];
      const ext = path.extname(file.originalname).toLowerCase();
      if (allowedTypes.includes(ext)) {
        cb(null, true);
      } else {
        cb(new Error('jpg, png, webp 형식의 커버 이미지만 지원됩니다'));
      }
    } else {
      cb(new Error('알 수 없는 파일 필드입니다'));
    }
  }
});

// POST /api/admin/uploads/cover - 上传聊天室封面
// 注意：此处路由在 /api/admin 下挂载，不能再拼接 Paths.Admin.Base，否则会变成 /api/admin/admin/uploads/cover
router.post('/uploads/cover', upload.single('cover-upload'), async (req, res) => {
  try {
    const file = req.file as Express.Multer.File
    if (!file) return res.status(400).json({ error: '커버 이미지를 업로드해주세요' })

    // 压缩图片
    await compressCoverImage(file)

    const coverUrl = `/files/covers/${file.filename}`
    return jsonResponse(res, { coverUrl })
  } catch (err) {
    console.error('커버 업로드 실패:', err)
    return res.status(500).json({ error: '커버 업로드에 실패했습니다' })
  }
})

// POST /api/admin/uploads/chat-image - 채팅방 이미지 업로드
router.post('/uploads/chat-image', upload.single('image'), async (req, res) => {
  try {
    const file = req.file as Express.Multer.File
    if (!file) return res.status(400).json({ error: '이미지 파일을 업로드해주세요' })

    // 이미지 파일 타입 확인
    if (!file.mimetype.startsWith('image/')) {
      return res.status(400).json({ error: '이미지 파일만 업로드 가능합니다' })
    }

    // 压缩图片
    await compressChatImage(file)

    const imageUrl = `/files/covers/${file.filename}` // covers 디렉토리 재사용
    return jsonResponse(res, { imageUrl })
  } catch (err) {
    console.error('채팅방 이미지 업로드 실패:', err)
    return res.status(500).json({ error: '채팅방 이미지 업로드에 실패했습니다' })
  }
})

// POST /api/admin/uploads/gift-icon - 上传礼物图标
router.post('/uploads/gift-icon', upload.single('icon'), async (req, res) => {
  try {
    const file = req.file as Express.Multer.File
    if (!file) return res.status(400).json({ error: '请上传图标文件' })

    // 检查文件类型
    if (!file.mimetype.startsWith('image/')) {
      return res.status(400).json({ error: '只允许上传图片文件' })
    }

    // 压缩图片
    await compressGiftIcon(file)

    const iconUrl = `/files/covers/${file.filename}` // 复用covers目录
    return jsonResponse(res, { iconUrl })
  } catch (err) {
    console.error('선물 아이콘 업로드 실패:', err)
    return res.status(500).json({ error: '선물 아이콘 업로드 실패' })
  }
})

// POST /api/admin/videos/upload - 上传视频文件
router.post(Paths.Admin.Videos + '/upload', videoUpload.fields([
  { name: 'video', maxCount: 1 },
  { name: 'cover', maxCount: 1 }
]), async (req, res) => {
  try {
    const files = req.files as { [fieldname: string]: Express.Multer.File[] };
    const { title, description, channelId } = req.body;

    if (!files.video || files.video.length === 0) {
      return res.status(400).json({ error: '비디오 파일을 업로드해주세요' });
    }

    if (!title || !channelId) {
      return res.status(400).json({ error: '제목과 채널 ID는 필수 항목입니다' });
    }

    const videoFile = files.video[0];
    const coverFile = files.cover?.[0];

    // 如果有封面文件，先压缩
    if (coverFile) {
      await compressCoverImage(coverFile);
    }

    // 파일 접근 URL 생성
    const fileUrl = `/files/videos/${videoFile.filename}`;
    const coverUrl = coverFile ? `/files/covers/${coverFile.filename}` : null;

    // 비디오 레코드 생성
    const video = await prisma.video.create({
      data: {
        channelId: BigInt(channelId),
        title,
        description: description || null,
        fileUrl,
        coverUrl,
        sizeBytes: BigInt(videoFile.size),
        status: 'published',
        publishedAt: new Date(),
      },
    });

    return jsonResponse(res, {
      id: video.id,
      title: video.title,
      fileUrl: video.fileUrl,
      coverUrl: video.coverUrl,
      sizeBytes: video.sizeBytes,
      createdAt: video.createdAt,
    });
  } catch (err) {
    console.error('비디오 업로드 실패:', err);
    return res.status(500).json({ error: '비디오 업로드에 실패했습니다' });
  }
});

// GET /api/admin/videos - 비디오 목록 조회
router.get(Paths.Admin.Videos, async (req, res) => {
  try {
    const { page = 1, limit = 20, channelId } = req.query as any;
    const offset = (Number(page) - 1) * Number(limit);

    const where: any = {}
    if (channelId) where.channelId = BigInt(channelId)

    const videos = await prisma.video.findMany({
      where,
      skip: offset,
      take: Number(limit),
      orderBy: { createdAt: 'desc' },
      include: {
        channel: { select: { id: true, name: true } },
      },
    });

    const total = await prisma.video.count({ where });

    return jsonResponse(res, {
      videos,
      total,
      page: Number(page),
      limit: Number(limit),
    });
  } catch (err) {
    console.error('비디오 목록 조회 실패:', err);
    return res.status(500).json({ error: '비디오 목록을 가져오는데 실패했습니다' });
  }
});

// ========== Channels (频道管理) ==========

// GET /api/admin/channels
router.get(Paths.Admin.Channels, async (req, res) => {
  try {
    const { page = 1, limit = 20, search } = req.query as any
    const p = Math.max(Number(page) || 1, 1)
    const l = Math.min(Math.max(Number(limit) || 20, 1), 100)

    const where: any = {}
    if (search) where.name = { contains: String(search), mode: 'insensitive' }

    const [channels, total] = await Promise.all([
      prisma.channel.findMany({ where, skip: (p - 1) * l, take: l, orderBy: { createdAt: 'desc' } }),
      prisma.channel.count({ where })
    ])

    return jsonResponse(res, { channels, total, page: p, limit: l })
  } catch (err) {
    console.error('채널 조회 실패:', err)
    return res.status(500).json({ error: '채널 조회 실패' })
  }
})

// POST /api/admin/channels
router.post(Paths.Admin.Channels, async (req, res) => {
  try {
    const { name, description, avatarUrl, isActive = true, ownerUserId } = req.body ?? {}
    if (!name) return res.status(400).json({ error: '채널 이름은 필수입니다' })

    // ownerUserId 파싱: 요청 본문에서 우선 사용, 없으면 시스템의 첫 번째 사용자를 기본 소유자로 설정
    let ownerId: bigint | null = null
    if (ownerUserId) {
      ownerId = BigInt(ownerUserId)
    } else {
      const anyUser = await prisma.user.findFirst({ select: { id: true }, orderBy: { id: 'asc' } })
      if (!anyUser) {
        return res.status(400).json({ error: '채널 소유자로 설정할 수 있는 사용자가 없습니다. 사용자를 먼저 생성하거나 요청에 ownerUserId를 포함해주세요' })
      }
      ownerId = anyUser.id as unknown as bigint
    }

    // 참고: seed에서 고정 id(1,2)를 강제 삽입했기 때문에 DB 자동 증가 시퀀스가 동기화되지 않을 수 있음
    // 현재 최대 id를 조회하고 +1하여 명시적으로 id를 지정함으로써 시퀀스 비동기화로 인한 P2002 충돌 방지
    const max = await prisma.channel.aggregate({ _max: { id: true } })
    const nextId = ((max as any)?._max?.id ?? 0n) + 1n

    const channel = await prisma.channel.create({
      data: { id: nextId, name, description, avatarUrl, isActive: Boolean(isActive), ownerUserId: ownerId }
    })
    return jsonResponse(res, channel)
  } catch (err) {
    console.error('채널 생성 실패:', err)
    return res.status(500).json({ error: '채널 생성 실패' })
  }
})

// PUT /api/admin/channels/:id
router.put(Paths.Admin.Channels + '/:id', async (req, res) => {
  try {
    const { id } = req.params as any
    const { name, description, avatarUrl, isActive } = req.body ?? {}

    const channel = await prisma.channel.update({
      where: { id: BigInt(id) },
      data: { name, description, avatarUrl, isActive }
    })
    return jsonResponse(res, channel)
  } catch (err) {
    console.error('채널 업데이트 실패:', err)
    return res.status(500).json({ error: '채널 업데이트 실패' })
  }
})

// DELETE /api/admin/channels/:id
router.delete(Paths.Admin.Channels + '/:id', async (req, res) => {
  try {
    const { id } = req.params as any
    const chId = BigInt(id)

    const [videoCount, liveRoom] = await Promise.all([
      prisma.video.count({ where: { channelId: chId } }),
      prisma.liveRoom.findUnique({ where: { channelId: chId } })
    ])

    if (videoCount > 0) return res.status(400).json({ error: '이 채널에 아직 비디오가 있어 삭제할 수 없습니다' })
    if (liveRoom) return res.status(400).json({ error: '이 채널이 이미 라이브룸과 연결되어 있어 삭제할 수 없습니다' })

    await prisma.channel.delete({ where: { id: chId } })
    return res.json({ success: true })
  } catch (err) {
    console.error('채널 삭제 실패:', err)
    return res.status(500).json({ error: '채널 삭제 실패' })
  }
})

// DELETE /api/admin/videos/:id - 删除视频
router.delete(Paths.Admin.Videos + '/:id', async (req, res) => {
  try {
    const { id } = req.params;

    const video = await prisma.video.findUnique({
      where: { id: BigInt(id) },
    });

    if (!video) {
      return res.status(404).json({ error: '비디오가 존재하지 않습니다' });
    }

    // 删除文件
    const videoPath = path.join(__dirname, '../../uploads/videos', path.basename(video.fileUrl));
    if (fs.existsSync(videoPath)) {
      fs.unlinkSync(videoPath);
    }

    if (video.coverUrl) {
      const coverPath = path.join(__dirname, '../../uploads/covers', path.basename(video.coverUrl));
      if (fs.existsSync(coverPath)) {
        fs.unlinkSync(coverPath);
      }
    }

    // 删除数据库记录

    await prisma.video.delete({
      where: { id: BigInt(id) },
    });

    return res.json({ success: true });
  } catch (err) {
    console.error('删除视频失败:', err);
    return res.status(500).json({ error: '删除视频失败' });
  }
});

// GET /api/admin/live-rooms - 获取直播间列表
router.get(Paths.Admin.LiveRooms, async (req, res) => {
  try {
    const { page = 1, limit = 20 } = req.query;
    const offset = (Number(page) - 1) * Number(limit);

    const liveRooms = await prisma.liveRoom.findMany({
      skip: offset,
      take: Number(limit),
      orderBy: { startedAt: 'desc' },
      include: {
        channel: {
          select: {
            id: true,
            name: true,
          },
        },
        replayVideo: {
          select: {
            id: true,
            title: true,
            fileUrl: true,
          },
        },
        presets: {
          where: { isActive: true },
          orderBy: { sortOrder: 'asc' },
        },
      },
    });

    const total = await prisma.liveRoom.count();

    return jsonResponse(res, {
      liveRooms,
      total,
      page: Number(page),
      limit: Number(limit),
    });
  } catch (err) {
    console.error('获取直播间列表失败:', err);
    return res.status(500).json({ error: '获取直播间列表失败' });
  }
});

// POST /api/admin/live-rooms - 创建直播间
router.post(Paths.Admin.LiveRooms, async (req, res) => {
  try {
    const { channelId, title, coverUrl, replayVideoId, manualViewerCount } = req.body;

    if (!channelId) {
      return res.status(400).json({ error: '频道ID为必填项' });
    }

    // 检查频道是否已有直播间
    const existingRoom = await prisma.liveRoom.findUnique({
      where: { channelId: BigInt(channelId) },
    });

    if (existingRoom) {
      return res.status(409).json({ error: '该频道已有直播间' });
    }

    const liveRoom = await prisma.liveRoom.create({
      data: {
        channelId: BigInt(channelId),
        title: title || null,
        coverUrl: coverUrl || null,
        replayVideoId: replayVideoId ? BigInt(replayVideoId) : null,
        manualViewerCount: manualViewerCount !== undefined ? Number(manualViewerCount) : 0,
      },
      include: {
        channel: {
          select: {
            id: true,
            name: true,
          },
        },
        replayVideo: {
          select: {
            id: true,
            title: true,
            fileUrl: true,
          },
        },
      },
    });

    return jsonResponse(res, liveRoom);
  } catch (err) {
    console.error('라이브 방 생성 실패:', err);
    return res.status(500).json({ error: '라이브 방 생성 실패' });
  }
});

// PUT /api/admin/live-rooms/:id - 更新直播间
router.put(Paths.Admin.LiveRooms + '/:id', async (req, res) => {
  try {
    const { id } = req.params as any;
    const { title, coverUrl, replayVideoId, status, channelId, manualViewerCount } = req.body ?? {};

    const roomId = BigInt(id)

    // 若传入 channelId，先校验目标频道是否已被其他直播间占用
    let updateData: any = {
      title: title !== undefined ? title : undefined,
      coverUrl: coverUrl !== undefined ? coverUrl : undefined,
      replayVideoId: replayVideoId !== undefined ? (replayVideoId ? BigInt(replayVideoId) : null) : undefined,
      status: status !== undefined ? status : undefined,
      manualViewerCount: manualViewerCount !== undefined ? Number(manualViewerCount) : undefined,
    }

    if (channelId !== undefined) {
      const chId = BigInt(channelId)
      const existing = await prisma.liveRoom.findUnique({ where: { channelId: chId } })
      // 允许把频道改到当前房间绑定的同一频道；若被其他房间占用则拒绝
      if (existing && existing.id !== roomId) {
        return res.status(409).json({ error: '해당 채널이 이미 다른 라이브 방에 연결되어 있습니다' })
      }
      updateData.channelId = chId
    }

    const liveRoom = await prisma.liveRoom.update({
      where: { id: roomId },
      data: updateData,
      include: {
        channel: { select: { id: true, name: true } },
        replayVideo: { select: { id: true, title: true, fileUrl: true } },
      },
    });

    return jsonResponse(res, liveRoom);
  } catch (err) {
    console.error('更新直播间失败:', err);
    return res.status(500).json({ error: '更新直播间失败' });
  }
});

// DELETE /api/admin/live-rooms/:id - 删除直播间
router.delete(Paths.Admin.LiveRooms + '/:id', async (req, res) => {
  try {
    const { id } = req.params;

    await prisma.liveRoom.delete({
      where: { id: BigInt(id) },
    });

    return res.json({ success: true });
  } catch (err) {
    console.error('删除直播间失败:', err);
    return res.status(500).json({ error: '删除直播间失败' });
  }
});

// GET /api/admin/live-rooms/:id/presets - 获取直播间预设消息
router.get(Paths.Admin.LiveRooms + '/:id/presets', async (req, res) => {
  try {
    const { id } = req.params;

    const presets = await prisma.liveMessagePreset.findMany({
      where: { liveRoomId: BigInt(id) },
      orderBy: { sortOrder: 'asc' },
    });

    return jsonResponse(res, { presets });
  } catch (err) {
    console.error('프리셋 메시지 조회 실패:', err);
    return res.status(500).json({ error: '프리셋 메시지 조회 실패' });
  }
});

// POST /api/admin/live-rooms/:id/presets - 创建预设消息
router.post(Paths.Admin.LiveRooms + '/:id/presets', async (req, res) => {
  try {
    const { id } = req.params;
    const { content, sortOrder } = req.body;

    if (!content) {
      return res.status(400).json({ error: '消息内容为必填项' });
    }

    const liveRoomId = BigInt(id);
    let finalSortOrder = Number(sortOrder);

    // 如果没有指定sortOrder或指定的sortOrder已存在，自动分配一个新的
    if (sortOrder === undefined || sortOrder === null) {
      // 获取当前最大的sortOrder值
      const maxSortOrderResult = await prisma.liveMessagePreset.aggregate({
        where: { liveRoomId },
        _max: { sortOrder: true },
      });
      finalSortOrder = (maxSortOrderResult._max.sortOrder || -1) + 1;
    } else {
      // 检查指定的sortOrder是否已存在
      const existingPreset = await prisma.liveMessagePreset.findFirst({
        where: {
          liveRoomId,
          sortOrder: finalSortOrder
        },
      });

      if (existingPreset) {
        // 如果已存在，获取最大sortOrder并加1
        const maxSortOrderResult = await prisma.liveMessagePreset.aggregate({
          where: { liveRoomId },
          _max: { sortOrder: true },
        });
        finalSortOrder = (maxSortOrderResult._max.sortOrder || -1) + 1;
      }
    }

    const preset = await prisma.liveMessagePreset.create({
      data: {
        liveRoomId,
        content,
        sortOrder: finalSortOrder,
      },
    });

    return jsonResponse(res, preset);
  } catch (err) {
    console.error('创建预设消息失败:', err);
    return res.status(500).json({ error: '创建预设消息失败' });
  }
});

// PUT /api/admin/live-rooms/:roomId/presets/:id - 프리셋 메시지 업데이트
router.put(Paths.Admin.LiveRooms + '/:roomId/presets/:id', async (req, res) => {
  try {
    const { id } = req.params;
    const { content, sortOrder, isActive } = req.body;

    const preset = await prisma.liveMessagePreset.update({
      where: { id: BigInt(id) },
      data: {
        content: content !== undefined ? content : undefined,
        sortOrder: sortOrder !== undefined ? Number(sortOrder) : undefined,
        isActive: isActive !== undefined ? isActive : undefined,
      },
    });

    return jsonResponse(res, preset);
  } catch (err) {
    console.error('프리셋 메시지 업데이트 실패:', err);
    return res.status(500).json({ error: '프리셋 메시지 업데이트에 실패했습니다' });
  }
});

// DELETE /api/admin/live-rooms/:roomId/presets/:id - 프리셋 메시지 삭제
router.delete(Paths.Admin.LiveRooms + '/:roomId/presets/:id', async (req, res) => {
  try {
    const { id } = req.params;

    await prisma.liveMessagePreset.delete({
      where: { id: BigInt(id) },
    });

    return res.json({ success: true });
  } catch (err) {
    console.error('프리셋 메시지 삭제 실패:', err);
    return res.status(500).json({ error: '프리셋 메시지 삭제에 실패했습니다' });
  }
});

// ========== Chat Rooms (独立聊天室管理) ==========

// GET /api/admin/chat-rooms
router.get(Paths.Admin.ChatRooms, async (req, res) => {
  try {
    const { page = 1, limit = 20, search } = req.query as any
    const p = Math.max(Number(page) || 1, 1)
    const l = Math.min(Math.max(Number(limit) || 20, 1), 100)
    const where: any = {}
    if (search) where.title = { contains: String(search), mode: 'insensitive' }

    // 权限过滤：partner角色只能看到自己创建的聊天室
    if (req.user?.role === 'partner') {
      where.creatorId = req.user.id;
    }

    const [rooms, total] = await Promise.all([
      prisma.chatRoom.findMany({
        where,
        skip: (p - 1) * l,
        take: l,
        orderBy: { createdAt: 'desc' },
        include: {
          creator: {
            select: {
              id: true,
              loginId: true,
              nickname: true,
              name: true
            }
          }
        }
      }),
      prisma.chatRoom.count({ where })
    ])

    return jsonResponse(res, { rooms, total, page: p, limit: l })
  } catch (err) {
    console.error('获取聊天室失败:', err)
    return res.status(500).json({ error: '获取聊天室失败' })
  }
})

// POST /api/admin/chat-rooms
router.post(Paths.Admin.ChatRooms, async (req, res) => {
  try {
    const { title, status = 'active', displayName, coverUrl, description, gender, capacity, password } = req.body ?? {}
    if (!title) return res.status(400).json({ error: '제목은 필수입니다' })

    const room = await prisma.chatRoom.create({
      data: {
        title,
        status,
        displayName,
        coverUrl,
        description,
        gender,
        capacity: capacity ? Number(capacity) : 1,
        password: password ? String(password).trim() || null : null,
        creatorId: req.user?.id // 记录创建者
      }
    })
    return jsonResponse(res, room)
  } catch (err) {
    console.error('채팅방 생성 실패:', err)
    return res.status(500).json({ error: '채팅방 생성 실패' })
  }
})

// PUT /api/admin/chat-rooms/:id
router.put(Paths.Admin.ChatRooms + '/:id', async (req, res) => {
  try {
    const { id } = req.params as any
    const { title, status, displayName, coverUrl, description, gender, capacity, password } = req.body ?? {}

    // 权限检查：partner角色只能编辑自己创建的聊天室
    if (req.user?.role === 'partner') {
      const room = await prisma.chatRoom.findUnique({
        where: { id: BigInt(id) },
        select: { creatorId: true },
      });

      if (!room || room.creatorId !== req.user.id) {
        return res.status(403).json({ error: '이 채팅방을 편집할 권한이 없습니다' });
      }
    }

    const room = await prisma.chatRoom.update({
      where: { id: BigInt(id) },
      data: {
        title,
        status,
        displayName,
        coverUrl,
        description,
        gender,
        capacity: capacity !== undefined ? Number(capacity) : undefined,
        password: password !== undefined ? (password ? String(password).trim() || null : null) : undefined
      }
    })
    return jsonResponse(res, room)
  } catch (err) {
    console.error('更新聊天室失败:', err)
    return res.status(500).json({ error: '更新聊天室失败' })
  }
})

// DELETE /api/admin/chat-rooms/:id
router.delete(Paths.Admin.ChatRooms + '/:id', async (req, res) => {
  try {
    const { id } = req.params as any

    // 权限检查：partner角色只能删除自己创建的聊天室
    if (req.user?.role === 'partner') {
      const room = await prisma.chatRoom.findUnique({
        where: { id: BigInt(id) },
        select: { creatorId: true },
      });

      if (!room || room.creatorId !== req.user.id) {
        return res.status(403).json({ error: '이 채팅방을 삭제할 권한이 없습니다' });
      }
    }

    await prisma.chatRoom.delete({ where: { id: BigInt(id) } })
    return res.json({ success: true })
  } catch (err) {
    console.error('删除聊天室失败:', err)
    return res.status(500).json({ error: '删除聊天室失败' })
  }
})

// 预设消息 CRUD
// GET /api/admin/chat-rooms/:id/presets
router.get(Paths.Admin.ChatRooms + '/:id/presets', async (req, res) => {
  try {
    const { id } = req.params as any

    // 权限检查：partner角色只能查看自己创建的聊天室的预设消息
    if (req.user?.role === 'partner') {
      const room = await prisma.chatRoom.findUnique({
        where: { id: BigInt(id) },
        select: { creatorId: true },
      });

      if (!room || room.creatorId !== req.user.id) {
        return res.status(403).json({ error: '无权查看此聊天室的预设消息' });
      }
    }

    const presets = await prisma.chatRoomPreset.findMany({ where: { roomId: BigInt(id) }, orderBy: { sortOrder: 'asc' } })
    return jsonResponse(res, { presets })
  } catch (err) {
    console.error('프리셋 조회 실패:', err)
    return res.status(500).json({ error: '프리셋 조회 실패' })
  }
})

// POST /api/admin/chat-rooms/:id/presets
router.post(Paths.Admin.ChatRooms + '/:id/presets', async (req, res) => {
  try {
    const { id } = req.params as any
    const { content, sortOrder } = req.body ?? {}
    if (!content) return res.status(400).json({ error: '内容必填' })

    // 权限检查：partner角色只能为自己创建的聊天室添加预设消息
    if (req.user?.role === 'partner') {
      const room = await prisma.chatRoom.findUnique({
        where: { id: BigInt(id) },
        select: { creatorId: true },
      });

      if (!room || room.creatorId !== req.user.id) {
        return res.status(403).json({ error: '이 채팅방에 프리셋 메시지를 추가할 권한이 없습니다' });
      }
    }

    const roomId = BigInt(id);
    let finalSortOrder = Number(sortOrder);

    // 如果没有指定sortOrder或指定的sortOrder已存在，自动分配一个新的
    if (sortOrder === undefined || sortOrder === null) {
      // 获取当前最大的sortOrder值
      const maxSortOrderResult = await prisma.chatRoomPreset.aggregate({
        where: { roomId },
        _max: { sortOrder: true },
      });
      finalSortOrder = (maxSortOrderResult._max.sortOrder || -1) + 1;
    } else {
      // 检查指定的sortOrder是否已存在
      const existingPreset = await prisma.chatRoomPreset.findFirst({
        where: {
          roomId,
          sortOrder: finalSortOrder
        },
      });

      if (existingPreset) {
        // 如果已存在，获取最大sortOrder并加1
        const maxSortOrderResult = await prisma.chatRoomPreset.aggregate({
          where: { roomId },
          _max: { sortOrder: true },
        });
        finalSortOrder = (maxSortOrderResult._max.sortOrder || -1) + 1;
      }
    }

    const preset = await prisma.chatRoomPreset.create({
      data: {
        roomId,
        content,
        sortOrder: finalSortOrder
      }
    })
    return jsonResponse(res, preset)
  } catch (err) {
    console.error('프리셋 생성 실패:', err)
    return res.status(500).json({ error: '프리셋 생성 실패' })
  }
})

// PUT /api/admin/chat-rooms/:roomId/presets/:id - 채팅방 프리셋 메시지 업데이트
router.put(Paths.Admin.ChatRooms + '/:roomId/presets/:id', async (req, res) => {
  try {
    const { id, roomId } = req.params as any
    const { content, sortOrder, isActive } = req.body ?? {}

    // 권한 확인: partner 역할은 자신이 생성한 채팅방의 프리셋 메시지만 업데이트할 수 있음
    if (req.user?.role === 'partner') {
      const room = await prisma.chatRoom.findUnique({
        where: { id: BigInt(roomId) },
        select: { creatorId: true },
      });

      if (!room || room.creatorId !== req.user.id) {
        return res.status(403).json({ error: '이 채팅방의 프리셋 메시지를 업데이트할 권한이 없습니다' });
      }
    }

    const preset = await prisma.chatRoomPreset.update({ where: { id: BigInt(id) }, data: { content, sortOrder: sortOrder !== undefined ? Number(sortOrder) : undefined, isActive } })
    return jsonResponse(res, preset)
  } catch (err) {
    console.error('프리셋 메시지 업데이트 실패:', err)
    return res.status(500).json({ error: '프리셋 메시지 업데이트에 실패했습니다' })
  }
})

// DELETE /api/admin/chat-rooms/:roomId/presets/:id - 채팅방 프리셋 메시지 삭제
router.delete(Paths.Admin.ChatRooms + '/:roomId/presets/:id', async (req, res) => {
  try {
    const { id, roomId } = req.params as any

    // 권한 확인: partner 역할은 자신이 생성한 채팅방의 프리셋 메시지만 삭제할 수 있음
    if (req.user?.role === 'partner') {
      const room = await prisma.chatRoom.findUnique({
        where: { id: BigInt(roomId) },
        select: { creatorId: true },
      });

      if (!room || room.creatorId !== req.user.id) {
        return res.status(403).json({ error: '이 채팅방의 프리셋 메시지를 삭제할 권한이 없습니다' });
      }
    }

    await prisma.chatRoomPreset.delete({ where: { id: BigInt(id) } })
    return res.json({ success: true })
  } catch (err) {
    console.error('프리셋 메시지 삭제 실패:', err)
    return res.status(500).json({ error: '프리셋 메시지 삭제에 실패했습니다' })
  }
})

// ========== Chat Messages (채팅방 메시지 관리) ==========

// GET /api/admin/chat-rooms/:id/messages - 채팅방 메시지 목록 조회
router.get(Paths.Admin.ChatRooms + '/:id/messages', async (req, res) => {
  try {
    const { id } = req.params as any
    const { page = 1, limit = 20, search } = req.query as any
    const p = Math.max(Number(page) || 1, 1)
    const l = Math.min(Math.max(Number(limit) || 20, 1), 100)

    // 권한 확인: partner 역할은 자신이 생성한 채팅방의 메시지만 조회할 수 있음
    if (req.user?.role === 'partner') {
      const room = await prisma.chatRoom.findUnique({
        where: { id: BigInt(id) },
        select: { creatorId: true },
      });

      if (!room || room.creatorId !== req.user.id) {
        return res.status(403).json({ error: '이 채팅방의 메시지를 조회할 권한이 없습니다' });
      }
    }

    const where: any = { roomId: BigInt(id), deletedAt: null }
    if (search) where.content = { contains: String(search), mode: 'insensitive' }

    const [messages, total] = await Promise.all([
      prisma.chatMessage.findMany({
        where,
        skip: (p - 1) * l,
        take: l,
        orderBy: { createdAt: 'desc' },
        include: {
          user: { select: { id: true, nickname: true, name: true, loginId: true, avatarUrl: true } },
        },
      }),
      prisma.chatMessage.count({ where })
    ])

    return jsonResponse(res, { messages, total, page: p, limit: l })
  } catch (err) {
    console.error('채팅방 메시지 목록 조회 실패:', err)
    return res.status(500).json({ error: '채팅방 메시지 목록을 조회하는데 실패했습니다' })
  }
})

// POST /api/admin/chat-rooms/:id/messages - 채팅방 메시지 생성
router.post(Paths.Admin.ChatRooms + '/:id/messages', async (req, res) => {
  try {
    const { id } = req.params as any
    const { content, messageType = 'text', imageUrl, isSystem = false } = req.body ?? {}

    // 메시지 내용 검증
    if (messageType === 'text') {
      if (!content) return res.status(400).json({ error: '메시지 내용은 필수입니다' })
    } else if (messageType === 'image') {
      if (!imageUrl) return res.status(400).json({ error: '이미지 URL은 필수입니다' })
    } else {
      return res.status(400).json({ error: '지원하지 않는 메시지 유형입니다' })
    }

    // 권한 확인: partner 역할은 자신이 생성한 채팅방에만 메시지를 추가할 수 있음
    if (req.user?.role === 'partner') {
      const room = await prisma.chatRoom.findUnique({
        where: { id: BigInt(id) },
        select: { creatorId: true },
      });

      if (!room || room.creatorId !== req.user.id) {
        return res.status(403).json({ error: '이 채팅방에 메시지를 추가할 권한이 없습니다' });
      }
    }

    const message = await prisma.chatMessage.create({
      data: {
        roomId: BigInt(id),
        content: messageType === 'text' ? String(content) : (imageUrl ? '이미지를 보냈습니다' : ''),
        messageType: String(messageType),
        imageUrl: messageType === 'image' ? String(imageUrl) : null,
        isSystem: Boolean(isSystem),
        userId: isSystem ? null : req.user?.id, // 시스템 메시지는 사용자와 연결하지 않음
      },
      include: {
        user: { select: { id: true, nickname: true, name: true, loginId: true, avatarUrl: true } },
      },
    })

    // SSE로 모든 연결된 클라이언트에게 메시지 브로드캐스트
    const payload = {
      id: String(message.id),
      content: message.content,
      messageType: message.messageType,
      imageUrl: message.imageUrl,
      username: isSystem ? '시스템 메시지' : (message.user?.nickname || message.user?.name || '관리자'),
      avatar: isSystem ? 'https://picsum.photos/32' : (message.user?.avatarUrl || 'https://picsum.photos/32'),
      timestamp: message.createdAt,
      senderUserId: message.userId ? String(message.userId) : undefined,
      isSystem: message.isSystem,
      isGift: false
    };

    const eventType = isSystem ? 'system' : 'message';
    sendToChatRoom(String(id), eventType, payload);

    return jsonResponse(res, message)
  } catch (err) {
    console.error('채팅방 메시지 생성 실패:', err)
    return res.status(500).json({ error: '채팅방 메시지 생성에 실패했습니다' })
  }
})

// PUT /api/admin/chat-rooms/:roomId/messages/:messageId - 채팅방 메시지 업데이트
router.put(Paths.Admin.ChatRooms + '/:roomId/messages/:messageId', async (req, res) => {
  try {
    const { roomId, messageId } = req.params as any
    const { content } = req.body ?? {}
    if (!content) return res.status(400).json({ error: '메시지 내용은 필수입니다' })

    // 권한 확인: partner 역할은 자신이 생성한 채팅방의 메시지만 업데이트할 수 있음
    if (req.user?.role === 'partner') {
      const room = await prisma.chatRoom.findUnique({
        where: { id: BigInt(roomId) },
        select: { creatorId: true },
      });

      if (!room || room.creatorId !== req.user.id) {
        return res.status(403).json({ error: '이 채팅방의 메시지를 업데이트할 권한이 없습니다' });
      }
    }

    const message = await prisma.chatMessage.update({
      where: { id: BigInt(messageId) },
      data: { content: String(content) },
      include: {
        user: { select: { id: true, nickname: true, name: true, loginId: true, avatarUrl: true } },
      },
    })

    return jsonResponse(res, message)
  } catch (err) {
    console.error('채팅방 메시지 업데이트 실패:', err)
    return res.status(500).json({ error: '채팅방 메시지 업데이트에 실패했습니다' })
  }
})

// DELETE /api/admin/chat-rooms/:roomId/messages/:messageId - 채팅방 메시지 삭제
router.delete(Paths.Admin.ChatRooms + '/:roomId/messages/:messageId', async (req, res) => {
  try {
    const { roomId, messageId } = req.params as any

    // 권한 확인: partner 역할은 자신이 생성한 채팅방의 메시지만 삭제할 수 있음
    if (req.user?.role === 'partner') {
      const room = await prisma.chatRoom.findUnique({
        where: { id: BigInt(roomId) },
        select: { creatorId: true },
      });

      if (!room || room.creatorId !== req.user.id) {
        return res.status(403).json({ error: '이 채팅방의 메시지를 삭제할 권한이 없습니다' });
      }
    }

    // 소프트 삭제: 실제 삭제 대신 deletedAt 필드 설정
    await prisma.chatMessage.update({
      where: { id: BigInt(messageId) },
      data: { deletedAt: new Date() }
    })

    return res.json({ success: true })
  } catch (err) {
    console.error('채팅방 메시지 삭제 실패:', err)
    return res.status(500).json({ error: '채팅방 메시지 삭제에 실패했습니다' })
  }
})

// ========== Notices (공지사항 관리) ==========

// GET /api/admin/notices
router.get(Paths.Admin.Notices, async (req, res) => {
  try {
    const { page = 1, limit = 20, q, isPublished } = req.query as any
    const p = Math.max(Number(page) || 1, 1)
    const l = Math.min(Math.max(Number(limit) || 20, 1), 100)

    const where: any = {}
    if (q) where.title = { contains: String(q), mode: 'insensitive' }
    if (isPublished !== undefined) where.isPublished = Boolean(isPublished === 'true')

    const [items, total] = await Promise.all([
      prisma.notice.findMany({
        where,
        skip: (p - 1) * l,
        take: l,
        orderBy: [{ isPinned: 'desc' }, { createdAt: 'desc' }]
      }),
      prisma.notice.count({ where })
    ])

    return jsonResponse(res, { items, total, page: p, limit: l })
  } catch (err) {
    console.error('공지사항 목록 조회 실패:', err)
    return res.status(500).json({ error: '공지사항 목록을 가져오는데 실패했습니다' })
  }
})

// GET /api/admin/notices/:id
router.get(Paths.Admin.Notices + '/:id', async (req, res) => {
  try {
    const { id } = req.params as any
    const notice = await prisma.notice.findUnique({ where: { id: BigInt(id) } })
    if (!notice) return res.status(404).json({ error: '공지사항이 존재하지 않습니다' })
    return jsonResponse(res, notice)
  } catch (err) {
    console.error('공지사항 상세 조회 실패:', err)
    return res.status(500).json({ error: '공지사항 상세 정보를 가져오는데 실패했습니다' })
  }
})

// POST /api/admin/notices
router.post(Paths.Admin.Notices, async (req, res) => {
  try {
    const { title, content, isPinned = false, isPublished = false } = req.body ?? {}
    if (!title || !content) return res.status(400).json({ error: '제목과 내용은 필수 항목입니다' })

    const notice = await prisma.notice.create({
      data: { title, content, isPinned: Boolean(isPinned), isPublished: Boolean(isPublished) }
    })
    return jsonResponse(res, notice)
  } catch (err) {
    console.error('공지사항 생성 실패:', err)
    return res.status(500).json({ error: '공지사항 생성에 실패했습니다' })
  }
})

// PUT /api/admin/notices/:id
router.put(Paths.Admin.Notices + '/:id', async (req, res) => {
  try {
    const { id } = req.params as any
    const { title, content, isPinned, isPublished } = req.body ?? {}

    const updateData: any = {}
    if (title !== undefined) updateData.title = title
    if (content !== undefined) updateData.content = content
    if (isPinned !== undefined) updateData.isPinned = Boolean(isPinned)
    if (isPublished !== undefined) updateData.isPublished = Boolean(isPublished)

    const notice = await prisma.notice.update({
      where: { id: BigInt(id) },
      data: updateData
    })
    return jsonResponse(res, notice)
  } catch (err) {
    console.error('공지사항 업데이트 실패:', err)
    return res.status(500).json({ error: '공지사항 업데이트에 실패했습니다' })
  }
})

// DELETE /api/admin/notices/:id
router.delete(Paths.Admin.Notices + '/:id', async (req, res) => {
  try {
    const { id } = req.params as any
    await prisma.notice.delete({ where: { id: BigInt(id) } })
    return res.json({ success: true })
  } catch (err) {
    console.error('공지사항 삭제 실패:', err)
    return res.status(500).json({ error: '공지사항 삭제에 실패했습니다' })
  }
})

// ========== FAQs (자주 묻는 질문 관리) ==========

// GET /api/admin/faqs
router.get(Paths.Admin.Faqs, async (req, res) => {
  try {
    const { page = 1, limit = 20, q, isPublished } = req.query as any
    const p = Math.max(Number(page) || 1, 1)
    const l = Math.min(Math.max(Number(limit) || 20, 1), 100)

    const where: any = {}
    if (q) where.title = { contains: String(q), mode: 'insensitive' }
    if (isPublished !== undefined) where.isPublished = Boolean(isPublished === 'true')

    const [items, total] = await Promise.all([
      prisma.faq.findMany({
        where,
        skip: (p - 1) * l,
        take: l,
        orderBy: [{ isPinned: 'desc' }, { createdAt: 'desc' }]
      }),
      prisma.faq.count({ where })
    ])

    return jsonResponse(res, { items, total, page: p, limit: l })
  } catch (err) {
    console.error('FAQ 목록 조회 실패:', err)
    return res.status(500).json({ error: 'FAQ 목록을 가져오는데 실패했습니다' })
  }
})

// GET /api/admin/faqs/:id
router.get(Paths.Admin.Faqs + '/:id', async (req, res) => {
  try {
    const { id } = req.params as any
    const faq = await prisma.faq.findUnique({ where: { id: BigInt(id) } })
    if (!faq) return res.status(404).json({ error: 'FAQ가 존재하지 않습니다' })
    return jsonResponse(res, faq)
  } catch (err) {
    console.error('FAQ 상세 조회 실패:', err)
    return res.status(500).json({ error: 'FAQ 상세 정보를 가져오는데 실패했습니다' })
  }
})

// POST /api/admin/faqs
router.post(Paths.Admin.Faqs, async (req, res) => {
  try {
    const { title, content, isPinned = false, isPublished = false } = req.body ?? {}
    if (!title || !content) return res.status(400).json({ error: '제목과 내용은 필수 항목입니다' })

    const faq = await prisma.faq.create({
      data: { title, content, isPinned: Boolean(isPinned), isPublished: Boolean(isPublished) }
    })
    return jsonResponse(res, faq)
  } catch (err) {
    console.error('FAQ 생성 실패:', err)
    return res.status(500).json({ error: 'FAQ 생성에 실패했습니다' })
  }
})

// PUT /api/admin/faqs/:id
router.put(Paths.Admin.Faqs + '/:id', async (req, res) => {
  try {
    const { id } = req.params as any
    const { title, content, isPinned, isPublished } = req.body ?? {}

    const updateData: any = {}
    if (title !== undefined) updateData.title = title
    if (content !== undefined) updateData.content = content
    if (isPinned !== undefined) updateData.isPinned = Boolean(isPinned)
    if (isPublished !== undefined) updateData.isPublished = Boolean(isPublished)

    const faq = await prisma.faq.update({
      where: { id: BigInt(id) },
      data: updateData
    })
    return jsonResponse(res, faq)
  } catch (err) {
    console.error('FAQ 업데이트 실패:', err)
    return res.status(500).json({ error: 'FAQ 업데이트에 실패했습니다' })
  }
})

// DELETE /api/admin/faqs/:id - FAQ 삭제
router.delete(Paths.Admin.Faqs + '/:id', async (req, res) => {
  try {
    const { id } = req.params as any
    await prisma.faq.delete({ where: { id: BigInt(id) } })
    return res.json({ success: true })
  } catch (err) {
    console.error('FAQ 삭제 실패:', err)
    return res.status(500).json({ error: 'FAQ 삭제에 실패했습니다' })
  }
})

// ====== 결제 관리 서브라우터 마운트 ======
router.use('/', adminPayRouter);

// ====== 출금 설정 (관리자) ======
router.get('/settings/withdraw', async (_req, res) => {
  try {
    const { getWithdrawSettings } = await import('@src/common/settings')
    const s = await getWithdrawSettings()
    return jsonResponse(res, s)
  } catch (err) {
    return res.status(500).json({ error: '출금 설정을 가져오는데 실패했습니다' })
  }
})
router.put('/settings/withdraw', async (req, res) => {
  try {
    const { setWithdrawSettings } = await import('@src/common/settings')
    const { rate, feePercent, minPoints, banks } = req.body ?? {}
    await setWithdrawSettings({
      rate: Number(rate),
      feePercent: Number(feePercent),
      minPoints: Number(minPoints) || 0,
      banks: Array.isArray(banks) ? banks.map(String) : [],
    })
    return jsonResponse(res, { ok: true })
  } catch (err) {
    return res.status(500).json({ error: '출금 설정 저장에 실패했습니다' })
  }
})

// ========== 파트너 관리 ==========

/**
 * 고유 추천 코드 생성
 * 형식: 6-8자리 대문자와 숫자 조합
 */
async function generateUniqueReferralCode(): Promise<string> {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  let attempts = 0;
  const maxAttempts = 100;

  while (attempts < maxAttempts) {
    let code = '';
    for (let i = 0; i < 6; i++) {
      code += chars.charAt(Math.floor(Math.random() * chars.length));
    }

    // 이미 존재하는지 확인
    const existing = await prisma.partnerProfile.findUnique({
      where: { referralCode: code }
    });

    if (!existing) {
      return code;
    }

    attempts++;
  }

  throw new Error('고유 추천 코드를 생성할 수 없습니다. 나중에 다시 시도해주세요');
}

// GET /api/admin/partners - 파트너 목록 조회 (관리자 전용)
router.get(Paths.Admin.Partners, requireAdmin, async (req, res) => {
  try {
    const { page = 1, limit = 20, search, status } = req.query as any;
    const p = Math.max(Number(page) || 1, 1);
    const l = Math.min(Math.max(Number(limit) || 20, 1), 100);

    const where: any = {
      profileType: 'partner', // 只显示合作伙伴类型
    };
    if (search) {
      where.OR = [
        { realName: { contains: String(search), mode: 'insensitive' } },
        { phoneNumber: { contains: String(search), mode: 'insensitive' } },
        { referralCode: { contains: String(search), mode: 'insensitive' } },
        { user: { loginId: { contains: String(search), mode: 'insensitive' } } },
        { user: { email: { contains: String(search), mode: 'insensitive' } } },
      ];
    }
    if (status !== undefined) {
      where.isActive = Boolean(status === 'true');
    }

    const [partners, total] = await Promise.all([
      prisma.partnerProfile.findMany({
        where,
        skip: (p - 1) * l,
        take: l,
        orderBy: { createdAt: 'desc' },
        include: {
          user: {
            select: {
              id: true,
              loginId: true,
              email: true,
              nickname: true,
              status: true,
              createdAt: true,
            },
          },
        },
      }),
      prisma.partnerProfile.count({ where }),
    ]);

    return jsonResponse(res, { partners, total, page: p, limit: l });
  } catch (err) {
    console.error('파트너 목록 조회 실패:', err);
    return res.status(500).json({ error: '파트너 목록을 가져오는데 실패했습니다' });
  }
});

// POST /api/admin/partners - 파트너 생성
router.post(Paths.Admin.Partners, requireAdmin, async (req, res) => {
  try {
    const { loginId, email, password, realName, phoneNumber, nickname } = req.body ?? {};

    if (!loginId || !email || !password || !realName || !phoneNumber) {
      return res.status(400).json({ error: 'loginId, email, password, realName, phoneNumber는 필수 항목입니다' });
    }

    // 移除전화번호 형식 검증 - 作为普通文本处理

    // 사용자 중복 확인
    const [existsLogin, existsEmail, existsPhoneNumber] = await Promise.all([
      prisma.user.findUnique({ where: { loginId }, select: { id: true } }),
      prisma.user.findUnique({ where: { email }, select: { id: true } }),
      prisma.partnerProfile.findFirst({ where: { phoneNumber }, select: { id: true } }),
    ]);

    if (existsLogin) return res.status(409).json({ error: '이미 존재하는 loginId입니다' });
    if (existsEmail) return res.status(409).json({ error: '이미 존재하는 email입니다' });
    if (existsPhoneNumber) return res.status(409).json({ error: '이미 존재하는 전화번호입니다' });

    // 고유 추천 코드 생성
    const referralCode = await generateUniqueReferralCode();
    const passwordHash = await bcrypt.hash(password, 10);

    // 트랜잭션으로 사용자와 파트너 프로필 생성
    const result = await prisma.$transaction(async (tx) => {
      const user = await tx.user.create({
        data: {
          loginId,
          email,
          passwordHash,
          nickname,
          role: 'partner',
          status: 'active',
        },
      });

      const partnerProfile = await tx.partnerProfile.create({
        data: {
          userId: user.id,
          realName,
          phoneNumber,
          referralCode,
          profileType: 'partner',
          isActive: true,
        },
        include: {
          user: {
            select: {
              id: true,
              loginId: true,
              email: true,
              nickname: true,
              status: true,
              createdAt: true,
            },
          },
        },
      });

      return partnerProfile;
    });

    return jsonResponse(res, result);
  } catch (err) {
    console.error('파트너 생성 실패:', err);
    return res.status(500).json({ error: '파트너 생성에 실패했습니다' });
  }
});

// GET /api/admin/partners/:id - 파트너 상세 정보 조회
router.get(Paths.Admin.Partners + '/:id', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params;
    const partner = await prisma.partnerProfile.findUnique({
      where: { id: BigInt(id) },
      include: {
        user: {
          select: {
            id: true,
            loginId: true,
            email: true,
            nickname: true,
            status: true,
            createdAt: true,
          },
        },
      },
    });

    if (!partner) {
      return res.status(404).json({ error: '파트너가 존재하지 않습니다' });
    }

    return jsonResponse(res, partner);
  } catch (err) {
    console.error('파트너 상세 정보 조회 실패:', err);
    return res.status(500).json({ error: '파트너 상세 정보를 가져오는데 실패했습니다' });
  }
});

// PUT /api/admin/partners/:id - 파트너 정보 업데이트
router.put(Paths.Admin.Partners + '/:id', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params;
    const { realName, phoneNumber, nickname, isActive } = req.body ?? {};

    const partner = await prisma.partnerProfile.findUnique({
      where: { id: BigInt(id) },
      include: { user: true },
    });

    if (!partner) {
      return res.status(404).json({ error: '파트너가 존재하지 않습니다' });
    }

    // 전화번호 업데이트 시 중복 확인 (형식 검증 제거)
    if (phoneNumber && phoneNumber !== partner.phoneNumber) {
      const existsPhoneNumber = await prisma.partnerProfile.findFirst({
        where: { phoneNumber, id: { not: BigInt(id) } },
        select: { id: true },
      });

      if (existsPhoneNumber) {
        return res.status(409).json({ error: '이미 존재하는 전화번호입니다' });
      }
    }

    // 트랜잭션으로 업데이트
    const result = await prisma.$transaction(async (tx) => {
      // 파트너 프로필 업데이트
      const updatedProfile = await tx.partnerProfile.update({
        where: { id: BigInt(id) },
        data: {
          realName: realName !== undefined ? realName : undefined,
          phoneNumber: phoneNumber !== undefined ? phoneNumber : undefined,
          isActive: isActive !== undefined ? Boolean(isActive) : undefined,
        },
      });

      // 사용자 정보 업데이트
      if (nickname !== undefined) {
        await tx.user.update({
          where: { id: partner.userId },
          data: { nickname },
        });
      }

      return updatedProfile;
    });

    // 전체 정보 다시 조회
    const updatedPartner = await prisma.partnerProfile.findUnique({
      where: { id: BigInt(id) },
      include: {
        user: {
          select: {
            id: true,
            loginId: true,
            email: true,
            nickname: true,
            status: true,
            createdAt: true,
          },
        },
      },
    });

    return jsonResponse(res, updatedPartner);
  } catch (err) {
    console.error('파트너 정보 업데이트 실패:', err);
    return res.status(500).json({ error: '파트너 정보 업데이트에 실패했습니다' });
  }
});

// PUT /api/admin/partners/:id/password - 重置合作伙伴密码
router.put(Paths.Admin.Partners + '/:id/password', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params;
    const { password } = req.body ?? {};

    if (!password || password.length < 8) {
      return res.status(400).json({ error: '密码至少8位' });
    }

    const partner = await prisma.partnerProfile.findUnique({
      where: { id: BigInt(id) },
      select: { userId: true },
    });

    if (!partner) {
      return res.status(404).json({ error: '合作伙伴不存在' });
    }

    const passwordHash = await bcrypt.hash(password, 10);

    await prisma.user.update({
      where: { id: partner.userId },
      data: { passwordHash },
    });

    return res.json({ success: true, message: '密码重置成功' });
  } catch (err) {
    console.error('重置密码失败:', err);
    return res.status(500).json({ error: '重置密码失败' });
  }
});

// DELETE /api/admin/partners/:id - 파트너 삭제 (级联删除)
router.delete(Paths.Admin.Partners + '/:id', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params;

    const partner = await prisma.partnerProfile.findUnique({
      where: { id: BigInt(id) },
      include: {
        user: {
          select: {
            id: true,
            loginId: true,
            role: true
          }
        }
      },
    });

    if (!partner) {
      return res.status(404).json({ error: '파트너가 존재하지 않습니다' });
    }

    // 管理员账户不能删除
    if (partner.user.role === 'admin') {
      return res.status(400).json({ error: '관리자 계정은 삭제할 수 없습니다' });
    }

    const userId = partner.userId;

    // 使用事务进行级联删除
    await prisma.$transaction(async (tx) => {
      // 1. 删除用户会话
      await tx.userSession.deleteMany({
        where: { userId: userId }
      });

      // 2. 删除OAuth账户
      await tx.oauthAccount.deleteMany({
        where: { userId: userId }
      });

      // 3. 删除视频观看记录
      await tx.videoView.deleteMany({
        where: { userId: userId }
      });

      // 4. 删除直播观看会话
      await tx.liveViewerSession.deleteMany({
        where: { userId: userId }
      });

      // 5. 删除直播消息
      await tx.liveMessage.deleteMany({
        where: { adminUserId: userId }
      });

      // 6. 删除聊天消息
      await tx.chatMessage.deleteMany({
        where: { userId: userId }
      });

      // 7. 删除聊天礼物发送记录
      await tx.chatGiftSend.deleteMany({
        where: { senderUserId: userId }
      });

      // 8. 删除礼物发送记录
      await tx.giftSend.deleteMany({
        where: { senderUserId: userId }
      });

      // 9. 删除积分记录
      await tx.pointLedger.deleteMany({
        where: { userId: userId }
      });

      // 10. 删除钱包
      await tx.userWallet.deleteMany({
        where: { userId: userId }
      });

      // 11. 删除充值记录
      await tx.recharge.deleteMany({
        where: { userId: userId }
      });

      // 12. 删除提现记录
      await tx.withdrawal.deleteMany({
        where: { userId: userId }
      });

      // 13. 删除用户创建的聊天室
      await tx.chatRoom.deleteMany({
        where: { creatorId: userId }
      });

      // 14. 删除用户拥有的频道（注意：这会级联删除频道下的所有内容）
      await tx.channel.deleteMany({
        where: { ownerUserId: userId }
      });

      // 15. 将被推荐用户的推荐人设为null
      await tx.user.updateMany({
        where: { referrerId: userId },
        data: { referrerId: null }
      });

      // 16. 删除合作伙伴资料
      await tx.partnerProfile.delete({
        where: { id: BigInt(id) }
      });

      // 17. 最后删除用户本身
      await tx.user.delete({
        where: { id: userId }
      });
    });

    return res.json({
      success: true,
      message: `파트너 "${partner.user.loginId}"가 모든 관련 데이터와 함께 완전히 삭제되었습니다`
    });
  } catch (err) {
    console.error('파트너 삭제 실패:', err);
    return res.status(500).json({ error: '파트너 삭제에 실패했습니다' });
  }
});

// ====== 체험판 상태 초기화 (관리자) ======
router.post('/users/:id/reset-trial', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params as any
    const userId = BigInt(id)
    const { clearLiveTrialState } = await import('@src/common/live-trial')
    await clearLiveTrialState(userId)
    return res.json({ success: true })
  } catch (err) {
    console.error('체험판 상태 초기화 실패:', err)
    return res.status(500).json({ error: '체험판 상태 초기화에 실패했습니다' })
  }
})

// GET /api/admin/trial-users - 페이지별 사용자 및 체험판 상태 조회
router.get('/trial-users', requireAdmin, async (req, res) => {
  try {
    const { page = 1, limit = 20, search } = req.query as any
    const p = Math.max(Number(page) || 1, 1)
    const l = Math.min(Math.max(Number(limit) || 20, 1), 100)

    const where: any = {}
    if (search) {
      where.OR = [
        { loginId: { contains: String(search), mode: 'insensitive' } },
        { email: { contains: String(search), mode: 'insensitive' } },
        { nickname: { contains: String(search), mode: 'insensitive' } },
        { name: { contains: String(search), mode: 'insensitive' } },
      ]
    }

    const [users, total] = await Promise.all([
      prisma.user.findMany({
        where,
        skip: (p - 1) * l,
        take: l,
        orderBy: { createdAt: 'desc' },
        select: {
          id: true,
          loginId: true,
          email: true,
          name: true,
          nickname: true,
          status: true,
          createdAt: true,
          wallet: { select: { pointsBalance: true } },
        },
      }),
      prisma.user.count({ where }),
    ])

    // AppSetting에서 체험판 상태 읽기
    const keys = users.map(u => `live.trial.user.${String(u.id)}`)
    const trials = keys.length ? await prisma.appSetting.findMany({ where: { key: { in: keys } } }) : []
    const trialMap = new Map<string, any>(trials.map(t => [t.key, t.value]))

    const items = users.map(u => ({
      user: u,
      trial: trialMap.get(`live.trial.user.${String(u.id)}`) || null,
    }))

    return jsonResponse(res, { items, total, page: p, limit: l })
  } catch (err) {
    console.error('체험판 사용자 목록 조회 실패:', err)
    return res.status(500).json({ error: '체험판 사용자 목록 조회에 실패했습니다' })
  }
})

// ========== 会员管理 ==========

// GET /api/admin/users - 회원 목록 조회
router.get(Paths.Admin.Users, async (req, res) => {
  try {
    const { page = 1, limit = 20, search, status, role, memberLevel, referrerId } = req.query as any;
    const p = Math.max(Number(page) || 1, 1);
    const l = Math.min(Math.max(Number(limit) || 20, 1), 100);

    const where: any = {};

    // 권한 필터: partner 역할은 자신이 추천한 사용자만 볼 수 있음
    if (req.user?.role === 'partner') {
      where.referrerId = req.user.id;
    } else if (referrerId) {
      where.referrerId = BigInt(referrerId);
    }

    // 검색 조건
    if (search) {
      where.OR = [
        { loginId: { contains: String(search), mode: 'insensitive' } },
        { email: { contains: String(search), mode: 'insensitive' } },
        { name: { contains: String(search), mode: 'insensitive' } },
        { nickname: { contains: String(search), mode: 'insensitive' } },
        { phone: { contains: String(search), mode: 'insensitive' } },
      ];
    }

    // 상태 필터
    if (status !== undefined) {
      where.status = String(status);
    }

    // 역할 필터
    if (role !== undefined) {
      where.role = String(role);
    }

    // 회원 등급 필터
    if (memberLevel && isValidMemberLevel(memberLevel)) {
      where.memberLevel = memberLevel;
    }

    const [users, total] = await Promise.all([
      prisma.user.findMany({
        where,
        skip: (p - 1) * l,
        take: l,
        orderBy: { createdAt: 'desc' },
        select: {
          id: true,
          loginId: true,
          email: true,
          name: true,
          nickname: true,
          phone: true,
          gender: true,
          birthdate: true,
          role: true,
          status: true,
          memberLevel: true,
          avatarUrl: true,
          createdAt: true,
          referrer: {
            select: {
              id: true,
              loginId: true,
              nickname: true,
              partnerProfile: {
                select: {
                  realName: true,
                  referralCode: true,
                },
              },
            },
          },
          partnerProfile: {
            select: {
              id: true,
              realName: true,
              referralCode: true,
              isActive: true,
            },
          },
          wallet: {
            select: {
              pointsBalance: true,
            },
          },
          _count: {
            select: {
              referrals: true,
            },
          },
        },
      }),
      prisma.user.count({ where }),
    ]);

    // AppSetting에서 시청 상태 읽기
    const keys = users.map(u => `live.trial.user.${String(u.id)}`)
    const trials = keys.length ? await prisma.appSetting.findMany({ where: { key: { in: keys } } }) : []
    const trialMap = new Map<string, any>(trials.map(t => [t.key, t.value]))

    const usersWithTrial = users.map(u => ({
      ...u,
      trial: trialMap.get(`live.trial.user.${String(u.id)}`) || null,
    }))

    return jsonResponse(res, { users: usersWithTrial, total, page: p, limit: l });
  } catch (err) {
    console.error('회원 목록 조회 실패:', err);
    return res.status(500).json({ error: '회원 목록을 가져오는데 실패했습니다' });
  }
});

// GET /api/admin/users/:id - 회원 상세 정보 조회
router.get(Paths.Admin.Users + '/:id', async (req, res) => {
  try {
    const { id } = req.params;
    const userId = BigInt(id);

    // 권한 확인: partner 역할은 자신이 추천한 사용자만 볼 수 있음
    if (req.user?.role === 'partner') {
      const user = await prisma.user.findUnique({
        where: { id: userId },
        select: { referrerId: true },
      });

      if (!user || user.referrerId !== req.user.id) {
        return res.status(403).json({ error: '이 사용자의 정보를 볼 수 있는 권한이 없습니다' });
      }
    }

    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: {
        id: true,
        loginId: true,
        email: true,
        name: true,
        nickname: true,
        phone: true,
        gender: true,
        birthdate: true,
        role: true,
        status: true,
        avatarUrl: true,
        createdAt: true,
        referrer: {
          select: {
            id: true,
            loginId: true,
            nickname: true,
            partnerProfile: {
              select: {
                realName: true,
                referralCode: true,
              },
            },
          },
        },
        wallet: {
          select: {
            pointsBalance: true,
            updatedAt: true,
          },
        },
        _count: {
          select: {
            referrals: true,
            sentGifts: true,
            chatSentGifts: true,
            recharges: true,
            withdrawals: true,
          },
        },
      },
    });

    if (!user) {
      return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
    }

    return jsonResponse(res, user);
  } catch (err) {
    console.error('회원 상세 정보 조회 실패:', err);
    return res.status(500).json({ error: '회원 상세 정보를 가져오는데 실패했습니다' });
  }
});

// POST /api/admin/users/:id/points/add - 포인트 추가 (관리자 및 합작伙伴)
router.post(Paths.Admin.Users + '/:id/points/add', requireAdminOrPartner, async (req, res) => {
  try {
    const { id } = req.params;
    const { points, note } = req.body ?? {};

    if (!points || points <= 0) {
      return res.status(400).json({ error: '포인트는 0보다 커야 합니다' });
    }

    const userId = BigInt(id);
    const pointsAmount = BigInt(points);

    // 사용자 존재 여부 확인
    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: { id: true, role: true, loginId: true, referrerId: true }
    });

    if (!user) {
      return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
    }

    // 权限检查：合作伙伴只能操作自己邀请的用户
    if (req.user?.role === 'partner') {
      if (user.referrerId !== req.user.id) {
        return res.status(403).json({ error: '이 사용자의 포인트를 관리할 권한이 없습니다' });
      }
    }

    // 트랜잭션으로 포인트 추가 처리
    const result = await prisma.$transaction(async (tx) => {
      // 지갑 업데이트 또는 생성
      const wallet = await tx.userWallet.upsert({
        where: { userId },
        update: {
          pointsBalance: {
            increment: pointsAmount
          }
        },
        create: {
          userId,
          pointsBalance: pointsAmount
        }
      });

      // 포인트 변동 기록
      const ledger = await tx.pointLedger.create({
        data: {
          userId,
          deltaPoints: pointsAmount,
          reason: req.user?.role === 'partner' ? 'partner_adjust' : 'admin_adjust',
          relatedType: req.user?.role === 'partner' ? 'partner' : 'admin',
          note: note || (req.user?.role === 'partner' ? '파트너 포인트 추가' : '관리자 포인트 추가')
        }
      });

      return { wallet, ledger };
    });

    return jsonResponse(res, {
      success: true,
      newBalance: result.wallet.pointsBalance,
      addedPoints: pointsAmount
    });
  } catch (err) {
    console.error('포인트 추가 실패:', err);
    return res.status(500).json({ error: '포인트 추가에 실패했습니다' });
  }
});

// GET /api/admin/users/:id/points/history - 포인트 내역 조회
router.get(Paths.Admin.Users + '/:id/points/history', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params;
    const { page = 1, limit = 50 } = req.query as any;
    const p = Math.max(Number(page) || 1, 1);
    const l = Math.min(Math.max(Number(limit) || 50, 1), 100);

    const userId = BigInt(id);

    // 사용자 존재 여부 확인
    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: { id: true }
    });

    if (!user) {
      return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
    }

    const records = await prisma.pointLedger.findMany({
      where: { userId },
      skip: (p - 1) * l,
      take: l,
      orderBy: { createdAt: 'desc' }
    });

    return jsonResponse(res, records);
  } catch (err) {
    console.error('포인트 내역 조회 실패:', err);
    return res.status(500).json({ error: '포인트 내역을 조회하는데 실패했습니다' });
  }
});

// POST /api/admin/users/:id/points/subtract - 포인트 차감 (관리자 및 합작伙伴)
router.post(Paths.Admin.Users + '/:id/points/subtract', requireAdminOrPartner, async (req, res) => {
  try {
    const { id } = req.params;
    const { points, note } = req.body ?? {};

    if (!points || points <= 0) {
      return res.status(400).json({ error: '포인트는 0보다 커야 합니다' });
    }

    const userId = BigInt(id);
    const pointsAmount = BigInt(points);

    // 사용자 존재 여부 확인
    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: {
        id: true,
        role: true,
        loginId: true,
        referrerId: true,
        wallet: { select: { pointsBalance: true } }
      }
    });

    if (!user) {
      return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
    }

    // 权限检查：合作伙伴只能操作自己邀请的用户
    if (req.user?.role === 'partner') {
      if (user.referrerId !== req.user.id) {
        return res.status(403).json({ error: '이 사용자의 포인트를 관리할 권한이 없습니다' });
      }
    }

    // 잔액 확인
    const currentBalance = user.wallet?.pointsBalance || BigInt(0);
    if (currentBalance < pointsAmount) {
      return res.status(400).json({ error: '포인트 잔액이 부족합니다' });
    }

    // 트랜잭션으로 포인트 차감 처리
    const result = await prisma.$transaction(async (tx) => {
      // 지갑 업데이트
      const wallet = await tx.userWallet.update({
        where: { userId },
        data: {
          pointsBalance: {
            decrement: pointsAmount
          }
        }
      });

      // 포인트 변동 기록
      const ledger = await tx.pointLedger.create({
        data: {
          userId,
          deltaPoints: -pointsAmount,
          reason: req.user?.role === 'partner' ? 'partner_adjust' : 'admin_adjust',
          relatedType: req.user?.role === 'partner' ? 'partner' : 'admin',
          note: note || (req.user?.role === 'partner' ? '파트너 포인트 차감' : '관리자 포인트 차감')
        }
      });

      return { wallet, ledger };
    });

    return jsonResponse(res, {
      success: true,
      newBalance: result.wallet.pointsBalance,
      subtractedPoints: pointsAmount
    });
  } catch (err) {
    console.error('포인트 차감 실패:', err);
    return res.status(500).json({ error: '포인트 차감에 실패했습니다' });
  }
});

// PUT /api/admin/users/:id - 사용자 기본 정보 업데이트 (관리자 전용)
router.put(Paths.Admin.Users + '/:id', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params;
    const { name, nickname, phone, gender, birthdate } = req.body ?? {};

    if (gender !== undefined && !['male', 'female', 'other', null, ''].includes(gender)) {
      return res.status(400).json({ error: '성별 값이 올바르지 않습니다' });
    }

    const data: any = {};
    if (name !== undefined) data.name = name === null ? null : String(name);
    if (nickname !== undefined) data.nickname = nickname === null ? null : String(nickname);
    if (phone !== undefined) data.phone = phone === null ? null : String(phone);
    if (gender !== undefined) data.gender = gender || null;
    if (birthdate !== undefined) {
      data.birthdate = birthdate ? new Date(birthdate) : null;
    }

    const updated = await prisma.user.update({
      where: { id: BigInt(id) },
      data,
      select: {
        id: true,
        loginId: true,
        email: true,
        name: true,
        nickname: true,
        phone: true,
        gender: true,
        birthdate: true,
        role: true,
        status: true,
        createdAt: true,
      },
    });

    return jsonResponse(res, { user: updated });
  } catch (err) {
    console.error('사용자 정보 업데이트 실패:', err);
    return res.status(500).json({ error: '사용자 정보 업데이트에 실패했습니다' });
  }
});

// PUT /api/admin/users/:id/status - 사용자 상태 업데이트 (관리자 전용)
router.put(Paths.Admin.Users + '/:id/status', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params;
    const { status } = req.body ?? {};

    if (!['active', 'banned'].includes(status)) {
      return res.status(400).json({ error: '상태 값이 올바르지 않습니다' });
    }

    const user = await prisma.user.update({
      where: { id: BigInt(id) },
      data: { status },
      select: {
        id: true,
        loginId: true,
        status: true,
      },
    });

    return jsonResponse(res, user);
  } catch (err) {
    console.error('사용자 상태 업데이트 실패:', err);
    return res.status(500).json({ error: '사용자 상태 업데이트에 실패했습니다' });
  }
});

// PUT /api/admin/users/:id/member-level - 사용자 회원 등급 업데이트 (관리자 전용)
router.put(Paths.Admin.Users + '/:id/member-level', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params;
    const { memberLevel } = req.body ?? {};

    if (!memberLevel || !isValidMemberLevel(memberLevel)) {
      return res.status(400).json({ error: '유효한 회원 등급을 입력해주세요' });
    }

    const userId = BigInt(id);

    // 사용자 존재 확인
    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: { id: true, loginId: true, memberLevel: true }
    });

    if (!user) {
      return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
    }

    // 회원 등급 업데이트
    const updatedUser = await prisma.user.update({
      where: { id: userId },
      data: { memberLevel },
      select: {
        id: true,
        loginId: true,
        email: true,
        memberLevel: true,
        name: true,
        nickname: true
      }
    });

    return jsonResponse(res, {
      message: '회원 등급이 성공적으로 업데이트되었습니다',
      user: updatedUser
    });
  } catch (err) {
    console.error('회원 등급 업데이트 실패:', err);
    return res.status(500).json({ error: '회원 등급 업데이트에 실패했습니다' });
  }
});

// DELETE /api/admin/users/:id - 사용자 삭제 (관리자 전용) - 级联删除
router.delete(Paths.Admin.Users + '/:id', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params;
    const userId = BigInt(id);

    // 사용자 존재 확인
    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: {
        id: true,
        loginId: true,
        role: true
      }
    });

    if (!user) {
      return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
    }

    // 관리자 계정은 삭제할 수 없음
    if (user.role === 'admin') {
      return res.status(400).json({ error: '관리자 계정은 삭제할 수 없습니다' });
    }

    // 使用事务进行级联删除
    await prisma.$transaction(async (tx) => {
      // 1. 删除用户会话
      await tx.userSession.deleteMany({
        where: { userId: userId }
      });

      // 2. 删除OAuth账户
      await tx.oauthAccount.deleteMany({
        where: { userId: userId }
      });

      // 3. 删除视频观看记录
      await tx.videoView.deleteMany({
        where: { userId: userId }
      });

      // 4. 删除直播观看会话
      await tx.liveViewerSession.deleteMany({
        where: { userId: userId }
      });

      // 5. 删除直播消息
      await tx.liveMessage.deleteMany({
        where: { adminUserId: userId }
      });

      // 6. 删除聊天消息
      await tx.chatMessage.deleteMany({
        where: { userId: userId }
      });

      // 7. 删除聊天礼物发送记录
      await tx.chatGiftSend.deleteMany({
        where: { senderUserId: userId }
      });

      // 8. 删除礼物发送记录
      await tx.giftSend.deleteMany({
        where: { senderUserId: userId }
      });

      // 9. 删除积分记录
      await tx.pointLedger.deleteMany({
        where: { userId: userId }
      });

      // 10. 删除钱包
      await tx.userWallet.deleteMany({
        where: { userId: userId }
      });

      // 11. 删除充值记录
      await tx.recharge.deleteMany({
        where: { userId: userId }
      });

      // 12. 删除提现记录
      await tx.withdrawal.deleteMany({
        where: { userId: userId }
      });

      // 13. 删除合作伙伴资料
      await tx.partnerProfile.deleteMany({
        where: { userId: userId }
      });

      // 14. 删除用户创建的聊天室
      await tx.chatRoom.deleteMany({
        where: { creatorId: userId }
      });

      // 15. 删除用户拥有的频道（注意：这会级联删除频道下的所有内容）
      await tx.channel.deleteMany({
        where: { ownerUserId: userId }
      });

      // 16. 将被推荐用户的推荐人设为null
      await tx.user.updateMany({
        where: { referrerId: userId },
        data: { referrerId: null }
      });

      // 17. 最后删除用户本身
      await tx.user.delete({
        where: { id: userId }
      });
    });

    return res.json({
      success: true,
      message: `사용자 "${user.loginId}"가 모든 관련 데이터와 함께 완전히 삭제되었습니다`
    });
  } catch (err) {
    console.error('사용자 삭제 실패:', err);
    return res.status(500).json({ error: '사용자 삭제에 실패했습니다' });
  }
});

// GET /api/admin/member-levels - 회원 등급 목록 조회
router.get('/member-levels', requireAdminOrPartner, async (req, res) => {
  try {
    const levels = getAllMemberLevels();
    return jsonResponse(res, levels);
  } catch (err) {
    console.error('회원 등급 목록 조회 실패:', err);
    return res.status(500).json({ error: '회원 등급 목록 조회에 실패했습니다' });
  }
});

// POST /api/admin/make-admin - 临时API：将用户设置为管理员 (仅用于初始设置)
router.post('/make-admin', async (req, res) => {
  try {
    const { loginId, email } = req.body ?? {};

    if (!loginId && !email) {
      return res.status(400).json({ error: 'loginId 또는 email이 필요합니다' });
    }

    const where: any = {};
    if (loginId) where.loginId = loginId;
    if (email) where.email = email;

    const user = await prisma.user.findFirst({ where });
    if (!user) {
      return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
    }

    const updatedUser = await prisma.user.update({
      where: { id: user.id },
      data: { role: 'admin' },
      select: {
        id: true,
        loginId: true,
        email: true,
        role: true
      }
    });

    return jsonResponse(res, {
      message: '사용자가 관리자로 설정되었습니다',
      user: updatedUser
    });
  } catch (err) {
    console.error('관리자 설정 실패:', err);
    return res.status(500).json({ error: '관리자 설정에 실패했습니다' });
  }
});

// ========== 排名管理 ==========

// GET /api/admin/rankings - 순위 목록 가져오기
router.get(Paths.Admin.Rankings, requireAdminOrPartner, async (req, res) => {
  try {
    const { page = 1, limit = 20, period = 'all' } = req.query as any;
    const p = Math.max(Number(page) || 1, 1);
    const l = Math.min(Math.max(Number(limit) || 20, 1), 100);
    const offset = (p - 1) * l;

    const rankings = await prisma.ranking.findMany({
      where: { period: period as string },
      skip: offset,
      take: l,
      orderBy: { rankPosition: 'asc' },
      include: {
        channel: {
          select: {
            id: true,
            name: true,
            avatarUrl: true,
            owner: {
              select: {
                id: true,
                loginId: true,
                nickname: true,
              },
            },
          },
        },
      },
    });

    const total = await prisma.ranking.count({
      where: { period: period as string },
    });

    return jsonResponse(res, {
      rankings,
      total,
      page: p,
      limit: l,
      period,
    });
  } catch (err) {
    console.error('순위 목록 가져오기 실패:', err);
    return res.status(500).json({ error: '순위 목록을 가져오는데 실패했습니다' });
  }
});

// POST /api/admin/rankings - 순위 생성 또는 업데이트
router.post(Paths.Admin.Rankings, requireAdminOrPartner, async (req, res) => {
  try {
    const {
      channelId,
      period,
      rankPosition,
      viewCount = 0,
      giftCount = 0,
      watchSeconds = 0,
      score = 0,
      isManual = true
    } = req.body ?? {};

    if (!channelId || !period || rankPosition === undefined) {
      return res.status(400).json({ error: 'channelId, period, rankPosition은 필수 항목입니다' });
    }

    // 채널 존재 여부 확인
    const channel = await prisma.channel.findUnique({
      where: { id: BigInt(channelId) },
    });

    if (!channel) {
      return res.status(404).json({ error: '채널이 존재하지 않습니다' });
    }

    // upsert를 사용하여 순위 생성 또는 업데이트
    const ranking = await prisma.ranking.upsert({
      where: {
        channelId_period: {
          channelId: BigInt(channelId),
          period: period as string,
        },
      },
      update: {
        rankPosition: Number(rankPosition),
        viewCount: Number(viewCount),
        giftCount: Number(giftCount),
        watchSeconds: Number(watchSeconds),
        score: Number(score),
        isManual: Boolean(isManual),
        updatedAt: new Date(),
      },
      create: {
        channelId: BigInt(channelId),
        period: period as string,
        rankPosition: Number(rankPosition),
        viewCount: Number(viewCount),
        giftCount: Number(giftCount),
        watchSeconds: Number(watchSeconds),
        score: Number(score),
        isManual: Boolean(isManual),
      },
      include: {
        channel: {
          select: {
            id: true,
            name: true,
            avatarUrl: true,
          },
        },
      },
    });

    return jsonResponse(res, ranking);
  } catch (err) {
    console.error('순위 생성/업데이트 실패:', err);
    return res.status(500).json({ error: '순위 생성/업데이트에 실패했습니다' });
  }
});

// PUT /api/admin/rankings/:id - 순위 업데이트
router.put(Paths.Admin.Rankings + '/:id', requireAdminOrPartner, async (req, res) => {
  try {
    const { id } = req.params;
    const {
      rankPosition,
      viewCount,
      giftCount,
      watchSeconds,
      score,
      isManual
    } = req.body ?? {};

    const updateData: any = { updatedAt: new Date() };

    if (rankPosition !== undefined) updateData.rankPosition = Number(rankPosition);
    if (viewCount !== undefined) updateData.viewCount = Number(viewCount);
    if (giftCount !== undefined) updateData.giftCount = Number(giftCount);
    if (watchSeconds !== undefined) updateData.watchSeconds = Number(watchSeconds);
    if (score !== undefined) updateData.score = Number(score);
    if (isManual !== undefined) updateData.isManual = Boolean(isManual);

    const ranking = await prisma.ranking.update({
      where: { id: BigInt(id) },
      data: updateData,
      include: {
        channel: {
          select: {
            id: true,
            name: true,
            avatarUrl: true,
          },
        },
      },
    });

    return jsonResponse(res, ranking);
  } catch (err) {
    console.error('순위 업데이트 실패:', err);
    return res.status(500).json({ error: '순위 업데이트에 실패했습니다' });
  }
});

// DELETE /api/admin/rankings/:id - 순위 삭제
router.delete(Paths.Admin.Rankings + '/:id', requireAdminOrPartner, async (req, res) => {
  try {
    const { id } = req.params;

    await prisma.ranking.delete({
      where: { id: BigInt(id) },
    });

    return jsonResponse(res, { success: true });
  } catch (err) {
    console.error('순위 삭제 실패:', err);
    return res.status(500).json({ error: '순위 삭제에 실패했습니다' });
  }
});

// POST /api/admin/rankings/batch - 순위 일괄 업데이트
router.post(Paths.Admin.Rankings + '/batch', requireAdminOrPartner, async (req, res) => {
  try {
    const { rankings, period } = req.body ?? {};

    if (!Array.isArray(rankings) || !period) {
      return res.status(400).json({ error: 'rankings 배열과 period는 필수 항목입니다' });
    }

    // 트랜잭션을 사용하여 일괄 업데이트
    const result = await prisma.$transaction(
      rankings.map((ranking: any) =>
        prisma.ranking.update({
          where: { id: BigInt(ranking.id) },
          data: {
            rankPosition: Number(ranking.rankPosition),
            updatedAt: new Date(),
          },
        })
      )
    );

    return jsonResponse(res, { updated: result.length });
  } catch (err) {
    console.error('순위 일괄 업데이트 실패:', err);
    return res.status(500).json({ error: '순위 일괄 업데이트에 실패했습니다' });
  }
});

// GET /api/admin/dashboard - 대시보드 통계 데이터 가져오기
router.get(Paths.Admin.Dashboard, requireAdminOrPartner, async (req, res) => {
  try {
    // 사용자 통계 가져오기
    const [
      totalUsers,
      activeUsers,
      partnersCount,
      todayNewUsers
    ] = await Promise.all([
      prisma.user.count(),
      prisma.user.count({ where: { status: 'active' } }),
      prisma.user.count({ where: { role: 'partner' } }),
      prisma.user.count({
        where: {
          createdAt: {
            gte: new Date(new Date().setHours(0, 0, 0, 0))
          }
        }
      })
    ]);

    // 충전 통계 가져오기
    const [
      totalRecharges,
      approvedRecharges,
      pendingRecharges,
      todayRecharges,
      totalRechargeAmount,
      approvedRechargeAmount
    ] = await Promise.all([
      prisma.recharge.count(),
      prisma.recharge.count({ where: { status: 'approved' } }),
      prisma.recharge.count({ where: { status: 'pending' } }),
      prisma.recharge.count({
        where: {
          createdAt: {
            gte: new Date(new Date().setHours(0, 0, 0, 0))
          }
        }
      }),
      prisma.recharge.aggregate({
        _sum: { amountDecimal: true }
      }),
      prisma.recharge.aggregate({
        where: { status: 'approved' },
        _sum: { amountDecimal: true }
      })
    ]);

    // 출금 통계 가져오기
    const [
      totalWithdrawals,
      approvedWithdrawals,
      pendingWithdrawals,
      totalWithdrawalAmount,
      approvedWithdrawalAmount
    ] = await Promise.all([
      prisma.withdrawal.count(),
      prisma.withdrawal.count({ where: { status: 'approved' } }),
      prisma.withdrawal.count({ where: { status: 'pending' } }),
      prisma.withdrawal.aggregate({
        _sum: { amountDecimal: true }
      }),
      prisma.withdrawal.aggregate({
        where: { status: 'approved' },
        _sum: { amountDecimal: true }
      })
    ]);

    // 선물 통계 가져오기
    const [
      totalGiftSends,
      todayGiftSends,
      totalGiftPoints,
      todayGiftPoints
    ] = await Promise.all([
      prisma.giftSend.count(),
      prisma.giftSend.count({
        where: {
          createdAt: {
            gte: new Date(new Date().setHours(0, 0, 0, 0))
          }
        }
      }),
      prisma.giftSend.aggregate({
        _sum: { totalPoints: true }
      }),
      prisma.giftSend.aggregate({
        where: {
          createdAt: {
            gte: new Date(new Date().setHours(0, 0, 0, 0))
          }
        },
        _sum: { totalPoints: true }
      })
    ]);

    // 채팅 선물 통계 가져오기
    const [
      totalChatGifts,
      todayChatGifts,
      totalChatGiftPoints,
      todayChatGiftPoints
    ] = await Promise.all([
      prisma.chatGiftSend.count(),
      prisma.chatGiftSend.count({
        where: {
          createdAt: {
            gte: new Date(new Date().setHours(0, 0, 0, 0))
          }
        }
      }),
      prisma.chatGiftSend.aggregate({
        _sum: { totalPoints: true }
      }),
      prisma.chatGiftSend.aggregate({
        where: {
          createdAt: {
            gte: new Date(new Date().setHours(0, 0, 0, 0))
          }
        },
        _sum: { totalPoints: true }
      })
    ]);

    // 채널 및 방송 통계 가져오기
    const [
      totalChannels,
      activeChannels,
      totalLiveRooms,
      activeLiveRooms,
      totalChatRooms,
      activeChatRooms
    ] = await Promise.all([
      prisma.channel.count(),
      prisma.channel.count({ where: { isActive: true } }),
      prisma.liveRoom.count(),
      prisma.liveRoom.count({ where: { status: 'active' } }),
      prisma.chatRoom.count(),
      prisma.chatRoom.count({ where: { status: 'active' } })
    ]);

    const dashboardStats = {
      users: {
        total: totalUsers,
        active: activeUsers,
        partners: partnersCount,
        todayNew: todayNewUsers
      },
      recharges: {
        total: totalRecharges,
        approved: approvedRecharges,
        pending: pendingRecharges,
        today: todayRecharges,
        totalAmount: totalRechargeAmount._sum.amountDecimal || 0,
        approvedAmount: approvedRechargeAmount._sum.amountDecimal || 0
      },
      withdrawals: {
        total: totalWithdrawals,
        approved: approvedWithdrawals,
        pending: pendingWithdrawals,
        totalAmount: totalWithdrawalAmount._sum.amountDecimal || 0,
        approvedAmount: approvedWithdrawalAmount._sum.amountDecimal || 0
      },
      gifts: {
        totalSends: totalGiftSends,
        todaySends: todayGiftSends,
        totalPoints: totalGiftPoints._sum.totalPoints || BigInt(0),
        todayPoints: todayGiftPoints._sum.totalPoints || BigInt(0),
        totalChatGifts: totalChatGifts,
        todayChatGifts: todayChatGifts,
        totalChatGiftPoints: totalChatGiftPoints._sum.totalPoints || BigInt(0),
        todayChatGiftPoints: todayChatGiftPoints._sum.totalPoints || BigInt(0)
      },
      content: {
        totalChannels,
        activeChannels,
        totalLiveRooms,
        activeLiveRooms,
        totalChatRooms,
        activeChatRooms
      }
    };

    return jsonResponse(res, dashboardStats);
  } catch (err) {
    console.error('대시보드 통계 가져오기 실패:', err);
    return res.status(500).json({ error: '대시보드 통계를 가져오는데 실패했습니다' });
  }
});

// GET /api/admin/user-logs - 사용자 활동 로그 가져오기
router.get(Paths.Admin.UserLogs, requireAdminOrPartner, async (req, res) => {
  try {
    const {
      page = 1,
      limit = 20,
      userId,
      search,
      type,
      startDate,
      endDate
    } = req.query as any;

    const p = Math.max(Number(page) || 1, 1);
    const l = Math.min(Math.max(Number(limit) || 20, 1), 100);
    const offset = (p - 1) * l;

    let targetUserId: bigint | undefined;

    // 사용자 ID가 제공된 경우 직접 사용
    if (userId) {
      targetUserId = BigInt(userId);
    }
    // 검색 키워드가 제공된 경우 사용자 검색
    else if (search) {
      const user = await prisma.user.findFirst({
        where: {
          OR: [
            { loginId: { contains: search, mode: 'insensitive' } },
            { email: { contains: search, mode: 'insensitive' } },
            { nickname: { contains: search, mode: 'insensitive' } },
            { name: { contains: search, mode: 'insensitive' } }
          ]
        },
        select: { id: true }
      });

      if (user) {
        targetUserId = user.id;
      } else {
        // 사용자를 찾지 못한 경우 빈 결과 반환
        return jsonResponse(res, {
          logs: [],
          total: 0,
          page: p,
          limit: l,
          user: null
        });
      }
    }

    // 날짜 필터 조건 구성
    const dateFilter: any = {};
    if (startDate) {
      dateFilter.gte = new Date(startDate);
    }
    if (endDate) {
      dateFilter.lte = new Date(endDate);
    }

    const logs: any[] = [];

    // 유형별 필터링 또는 모든 유형의 로그 가져오기
    const shouldInclude = (logType: string) => !type || type === 'all' || type === logType;

    // 충전 기록 가져오기
    if (shouldInclude('recharge')) {
      const rechargeWhere: any = {};
      if (targetUserId) rechargeWhere.userId = targetUserId;
      if (Object.keys(dateFilter).length > 0) rechargeWhere.createdAt = dateFilter;

      const recharges = await prisma.recharge.findMany({
        where: rechargeWhere,
        include: {
          user: {
            select: {
              id: true,
              loginId: true,
              nickname: true,
              name: true
            }
          }
        },
        orderBy: { createdAt: 'desc' },
        skip: offset,
        take: l
      });

      recharges.forEach(recharge => {
        logs.push({
          id: `recharge_${recharge.id}`,
          type: 'recharge',
          userId: recharge.userId,
          user: recharge.user,
          amount: recharge.amountDecimal,
          points: recharge.pointsCredited,
          status: recharge.status,
          method: recharge.method,
          referenceNo: recharge.referenceNo,
          createdAt: recharge.createdAt,
          description: `${recharge.amountDecimal}원 충전, ${recharge.pointsCredited} 포인트 획득`
        });
      });
    }

    // 출금 기록 가져오기
    if (shouldInclude('withdrawal')) {
      const withdrawalWhere: any = {};
      if (targetUserId) withdrawalWhere.userId = targetUserId;
      if (Object.keys(dateFilter).length > 0) withdrawalWhere.createdAt = dateFilter;

      const withdrawals = await prisma.withdrawal.findMany({
        where: withdrawalWhere,
        include: {
          user: {
            select: {
              id: true,
              loginId: true,
              nickname: true,
              name: true
            }
          }
        },
        orderBy: { createdAt: 'desc' },
        skip: offset,
        take: l
      });

      withdrawals.forEach(withdrawal => {
        logs.push({
          id: `withdrawal_${withdrawal.id}`,
          type: 'withdrawal',
          userId: withdrawal.userId,
          user: withdrawal.user,
          amount: withdrawal.amountDecimal,
          points: withdrawal.pointsToWithdraw,
          status: withdrawal.status,
          referenceNo: withdrawal.referenceNo,
          createdAt: withdrawal.createdAt,
          description: `${withdrawal.pointsToWithdraw} 포인트 출금, 금액 ${withdrawal.amountDecimal}원`
        });
      });
    }

    // 선물 발송 기록 가져오기
    if (shouldInclude('gift')) {
      const giftWhere: any = {};
      if (targetUserId) giftWhere.senderUserId = targetUserId;
      if (Object.keys(dateFilter).length > 0) giftWhere.createdAt = dateFilter;

      const giftSends = await prisma.giftSend.findMany({
        where: giftWhere,
        include: {
          sender: {
            select: {
              id: true,
              loginId: true,
              nickname: true,
              name: true
            }
          },
          gift: {
            select: {
              id: true,
              name: true,
              iconUrl: true
            }
          },
          channel: {
            select: {
              id: true,
              name: true
            }
          }
        },
        orderBy: { createdAt: 'desc' },
        skip: offset,
        take: l
      });

      giftSends.forEach(giftSend => {
        logs.push({
          id: `gift_${giftSend.id}`,
          type: 'gift',
          userId: giftSend.senderUserId,
          user: giftSend.sender,
          points: giftSend.totalPoints,
          quantity: giftSend.quantity,
          gift: giftSend.gift,
          channel: giftSend.channel,
          createdAt: giftSend.createdAt,
          description: `채널 ${giftSend.channel.name}에서 ${giftSend.gift.name} ${giftSend.quantity}개 발송, ${giftSend.totalPoints} 포인트 소비`
        });
      });
    }

    // 채팅 선물 기록 가져오기
    if (shouldInclude('chat_gift')) {
      const chatGiftWhere: any = {};
      if (targetUserId) chatGiftWhere.senderUserId = targetUserId;
      if (Object.keys(dateFilter).length > 0) chatGiftWhere.createdAt = dateFilter;

      const chatGifts = await prisma.chatGiftSend.findMany({
        where: chatGiftWhere,
        include: {
          sender: {
            select: {
              id: true,
              loginId: true,
              nickname: true,
              name: true
            }
          },
          receiver: {
            select: {
              id: true,
              loginId: true,
              nickname: true,
              name: true
            }
          },
          gift: {
            select: {
              id: true,
              name: true,
              iconUrl: true
            }
          },
          room: {
            select: {
              id: true,
              title: true
            }
          }
        },
        orderBy: { createdAt: 'desc' },
        skip: offset,
        take: l
      });

      chatGifts.forEach(chatGift => {
        logs.push({
          id: `chat_gift_${chatGift.id}`,
          type: 'chat_gift',
          userId: chatGift.senderUserId,
          user: chatGift.sender,
          receiver: chatGift.receiver,
          points: chatGift.totalPoints,
          quantity: chatGift.quantity,
          gift: chatGift.gift,
          room: chatGift.room,
          createdAt: chatGift.createdAt,
          description: `채팅방 ${chatGift.room.title || '이름 없음'}에서 ${chatGift.receiver?.nickname || chatGift.receiver?.name || '익명 사용자'}에게 ${chatGift.gift.name} ${chatGift.quantity}개 발송, ${chatGift.totalPoints} 포인트 소비`
        });
      });
    }

    // 시간순 정렬
    logs.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());

    // 사용자 정보 가져오기 (사용자가 지정된 경우)
    let userInfo = null;
    if (targetUserId) {
      userInfo = await prisma.user.findUnique({
        where: { id: targetUserId },
        select: {
          id: true,
          loginId: true,
          email: true,
          nickname: true,
          name: true,
          role: true,
          status: true,
          createdAt: true,
          wallet: {
            select: {
              pointsBalance: true
            }
          }
        }
      });
    }

    // 총 개수 계산 (여기서는 간단히 처리, 실제로는 각 유형별 총 개수를 계산해야 함)
    const total = logs.length;

    return jsonResponse(res, {
      logs: logs.slice(0, l),
      total,
      page: p,
      limit: l,
      user: userInfo
    });
  } catch (err) {
    console.error('사용자 로그 가져오기 실패:', err);
    return res.status(500).json({ error: '사용자 로그를 가져오는데 실패했습니다' });
  }
});

// ========== 管理员推荐码管理 ==========

// GET /api/admin/admin-referral - 获取当前管理员的推荐码
router.get('/admin-referral', requireAdmin, async (req, res) => {
  try {
    const adminProfile = await prisma.partnerProfile.findUnique({
      where: { userId: req.user!.id },
      select: {
        id: true,
        referralCode: true,
        profileType: true,
        isActive: true,
        createdAt: true,
        updatedAt: true,
      },
    });

    return jsonResponse(res, { profile: adminProfile });
  } catch (err) {
    console.error('获取管理员推荐码失败:', err);
    return res.status(500).json({ error: '获取推荐码失败' });
  }
});

// POST /api/admin/admin-referral - 为当前管理员生成推荐码
router.post('/admin-referral', requireAdmin, async (req, res) => {
  try {
    // 检查是否已经有推荐码
    const existingProfile = await prisma.partnerProfile.findUnique({
      where: { userId: req.user!.id },
    });

    if (existingProfile) {
      return res.status(409).json({ error: '管理员已经有推荐码了' });
    }

    // 生成唯一推荐码
    const referralCode = await generateUniqueReferralCode();

    // 创建管理员推荐码配置
    const adminProfile = await prisma.partnerProfile.create({
      data: {
        userId: req.user!.id,
        referralCode,
        profileType: 'admin',
        isActive: true,
      },
      select: {
        id: true,
        referralCode: true,
        profileType: true,
        isActive: true,
        createdAt: true,
        updatedAt: true,
      },
    });

    return jsonResponse(res, { profile: adminProfile });
  } catch (err) {
    console.error('生成管理员推荐码失败:', err);
    return res.status(500).json({ error: '生成推荐码失败' });
  }
});

// PUT /api/admin/admin-referral - 更新管理员推荐码状态
router.put('/admin-referral', requireAdmin, async (req, res) => {
  try {
    const { isActive } = req.body ?? {};

    const existingProfile = await prisma.partnerProfile.findUnique({
      where: { userId: req.user!.id },
    });

    if (!existingProfile) {
      return res.status(404).json({ error: '管理员还没有推荐码' });
    }

    const updatedProfile = await prisma.partnerProfile.update({
      where: { userId: req.user!.id },
      data: {
        isActive: isActive !== undefined ? Boolean(isActive) : undefined,
      },
      select: {
        id: true,
        referralCode: true,
        profileType: true,
        isActive: true,
        createdAt: true,
        updatedAt: true,
      },
    });

    return jsonResponse(res, { profile: updatedProfile });
  } catch (err) {
    console.error('更新管理员推荐码失败:', err);
    return res.status(500).json({ error: '更新推荐码失败' });
  }
});

// POST /api/admin/admin-referral/regenerate - 重新生成管理员推荐码
router.post('/admin-referral/regenerate', requireAdmin, async (req, res) => {
  try {
    const existingProfile = await prisma.partnerProfile.findUnique({
      where: { userId: req.user!.id },
    });

    if (!existingProfile) {
      return res.status(404).json({ error: '管理员还没有推荐码' });
    }

    // 生成新的唯一推荐码
    const newReferralCode = await generateUniqueReferralCode();

    const updatedProfile = await prisma.partnerProfile.update({
      where: { userId: req.user!.id },
      data: {
        referralCode: newReferralCode,
      },
      select: {
        id: true,
        referralCode: true,
        profileType: true,
        isActive: true,
        createdAt: true,
        updatedAt: true,
      },
    });

    return jsonResponse(res, { profile: updatedProfile });
  } catch (err) {
    console.error('重新生成管理员推荐码失败:', err);
    return res.status(500).json({ error: '重新生成推荐码失败' });
  }
});

// GET /api/admin/admin-referral/stats - 获取管理员推荐码统计数据
router.get('/admin-referral/stats', requireAdmin, async (req, res) => {
  try {
    // 获取当前管理员的推荐码配置
    const adminProfile = await prisma.partnerProfile.findUnique({
      where: { userId: req.user!.id },
      select: {
        id: true,
        referralCode: true,
        isActive: true,
      },
    });

    if (!adminProfile) {
      return res.status(404).json({ error: '管理员还没有推荐码' });
    }

    // 获取本月开始时间
    const now = new Date();
    const thisMonthStart = new Date(now.getFullYear(), now.getMonth(), 1);

    // 查询推荐统计数据
    const [totalReferrals, activeReferrals, thisMonthReferrals] = await Promise.all([
      // 总推荐会员数：通过该管理员推荐码注册的用户总数
      prisma.user.count({
        where: {
          referrer: {
            partnerProfile: {
              referralCode: adminProfile.referralCode,
            },
          },
        },
      }),

      // 活跃推荐会员数：状态为active的推荐用户数
      prisma.user.count({
        where: {
          referrer: {
            partnerProfile: {
              referralCode: adminProfile.referralCode,
            },
          },
          status: 'active',
        },
      }),

      // 本月新增推荐数：本月通过该推荐码注册的用户数
      prisma.user.count({
        where: {
          referrer: {
            partnerProfile: {
              referralCode: adminProfile.referralCode,
            },
          },
          createdAt: {
            gte: thisMonthStart,
          },
        },
      }),
    ]);

    const stats = {
      totalReferrals,
      activeReferrals,
      thisMonthReferrals,
    };

    return jsonResponse(res, stats);
  } catch (err) {
    console.error('获取管理员推荐统计失败:', err);
    return res.status(500).json({ error: '获取推荐统计失败' });
  }
});

// ========== 广告管理 ==========

// GET /api/admin/advertisements - 获取广告列表
router.get(Paths.Admin.Advertisements, async (req, res) => {
  try {
    const { page = 1, limit = 20, search, isActive } = req.query as any;
    const p = Math.max(Number(page) || 1, 1);
    const l = Math.min(Math.max(Number(limit) || 20, 1), 100);

    const where: any = {};
    if (search) {
      where.title = { contains: String(search), mode: 'insensitive' };
    }
    if (isActive !== undefined) {
      where.isActive = Boolean(isActive);
    }

    const [advertisements, total] = await Promise.all([
      prisma.advertisement.findMany({
        where,
        skip: (p - 1) * l,
        take: l,
        orderBy: [{ sortOrder: 'asc' }, { createdAt: 'desc' }],
      }),
      prisma.advertisement.count({ where }),
    ]);

    return jsonResponse(res, { advertisements, total, page: p, limit: l });
  } catch (err) {
    console.error('广告列表获取失败:', err);
    return res.status(500).json({ error: '广告列表获取失败' });
  }
});

// GET /api/admin/advertisements/active - 获取活跃广告列表（前端轮播使用）
router.get(Paths.Admin.Advertisements + '/active', async (req, res) => {
  try {
    const now = new Date();
    const advertisements = await prisma.advertisement.findMany({
      where: {
        isActive: true,
        OR: [
          { startDate: null, endDate: null },
          { startDate: null, endDate: { gte: now } },
          { startDate: { lte: now }, endDate: null },
          { startDate: { lte: now }, endDate: { gte: now } },
        ],
      },
      orderBy: [{ sortOrder: 'asc' }, { createdAt: 'desc' }],
    });

    return jsonResponse(res, { advertisements });
  } catch (err) {
    console.error('活跃广告获取失败:', err);
    return res.status(500).json({ error: '活跃广告获取失败' });
  }
});

// POST /api/admin/advertisements - 创建广告
router.post(Paths.Admin.Advertisements, requireAdmin, async (req, res) => {
  try {
    const {
      title,
      description,
      imageUrl,
      linkUrl,
      sortOrder = 0,
      isActive = true,
      startDate,
      endDate,
    } = req.body;

    if (!title || !imageUrl) {
      return res.status(400).json({ error: '标题和图片URL为必填项' });
    }

    const advertisement = await prisma.advertisement.create({
      data: {
        title,
        description,
        imageUrl,
        linkUrl,
        sortOrder: Number(sortOrder),
        isActive: Boolean(isActive),
        startDate: startDate ? new Date(startDate) : null,
        endDate: endDate ? new Date(endDate) : null,
      },
    });

    return jsonResponse(res, advertisement);
  } catch (err) {
    console.error('广告创建失败:', err);
    return res.status(500).json({ error: '广告创建失败' });
  }
});

// PUT /api/admin/advertisements/:id - 更新广告
router.put(Paths.Admin.Advertisements + '/:id', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params;
    const {
      title,
      description,
      imageUrl,
      linkUrl,
      sortOrder,
      isActive,
      startDate,
      endDate,
    } = req.body;

    const updateData: any = {};
    if (title !== undefined) updateData.title = title;
    if (description !== undefined) updateData.description = description;
    if (imageUrl !== undefined) updateData.imageUrl = imageUrl;
    if (linkUrl !== undefined) updateData.linkUrl = linkUrl;
    if (sortOrder !== undefined) updateData.sortOrder = Number(sortOrder);
    if (isActive !== undefined) updateData.isActive = Boolean(isActive);
    if (startDate !== undefined) updateData.startDate = startDate ? new Date(startDate) : null;
    if (endDate !== undefined) updateData.endDate = endDate ? new Date(endDate) : null;

    const advertisement = await prisma.advertisement.update({
      where: { id: BigInt(id) },
      data: updateData,
    });

    return jsonResponse(res, advertisement);
  } catch (err) {
    console.error('广告更新失败:', err);
    return res.status(500).json({ error: '广告更新失败' });
  }
});

// DELETE /api/admin/advertisements/:id - 删除广告
router.delete(Paths.Admin.Advertisements + '/:id', requireAdmin, async (req, res) => {
  try {
    const { id } = req.params;

    await prisma.advertisement.delete({
      where: { id: BigInt(id) },
    });

    return jsonResponse(res, { success: true });
  } catch (err) {
    console.error('广告删除失败:', err);
    return res.status(500).json({ error: '广告删除失败' });
  }
});

// POST /api/admin/advertisements/:id/click - 记录广告点击
router.post(Paths.Admin.Advertisements + '/:id/click', async (req, res) => {
  try {
    const { id } = req.params;

    await prisma.advertisement.update({
      where: { id: BigInt(id) },
      data: { clickCount: { increment: 1 } },
    });

    return jsonResponse(res, { success: true });
  } catch (err) {
    console.error('广告点击记录失败:', err);
    return res.status(500).json({ error: '广告点击记录失败' });
  }
});

export default router;
